aboutsummaryrefslogtreecommitdiff
path: root/pkg/images/immatch
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2015-07-08 20:46:52 -0400
commitfa080de7afc95aa1c19a6e6fc0e0708ced2eadc4 (patch)
treebdda434976bc09c864f2e4fa6f16ba1952b1e555 /pkg/images/immatch
downloadiraf-linux-fa080de7afc95aa1c19a6e6fc0e0708ced2eadc4.tar.gz
Initial commit
Diffstat (limited to 'pkg/images/immatch')
-rw-r--r--pkg/images/immatch/Revisions2025
-rw-r--r--pkg/images/immatch/doc/geomap.hlp435
-rw-r--r--pkg/images/immatch/doc/geotran.hlp320
-rw-r--r--pkg/images/immatch/doc/geoxytran.hlp408
-rw-r--r--pkg/images/immatch/doc/gregister.hlp265
-rw-r--r--pkg/images/immatch/doc/imalign.hlp316
-rw-r--r--pkg/images/immatch/doc/imcentroid.hlp257
-rw-r--r--pkg/images/immatch/doc/imcombine.hlp1471
-rw-r--r--pkg/images/immatch/doc/linmatch.hlp699
-rw-r--r--pkg/images/immatch/doc/psfmatch.hlp595
-rw-r--r--pkg/images/immatch/doc/skymap.hlp642
-rw-r--r--pkg/images/immatch/doc/skyxymatch.hlp406
-rw-r--r--pkg/images/immatch/doc/sregister.hlp779
-rw-r--r--pkg/images/immatch/doc/wcscopy.hlp80
-rw-r--r--pkg/images/immatch/doc/wcsmap.hlp619
-rw-r--r--pkg/images/immatch/doc/wcsxymatch.hlp314
-rw-r--r--pkg/images/immatch/doc/wregister.hlp761
-rw-r--r--pkg/images/immatch/doc/xregister.hlp707
-rw-r--r--pkg/images/immatch/doc/xyxymatch.hlp468
-rw-r--r--pkg/images/immatch/geomap.par32
-rw-r--r--pkg/images/immatch/geotran.par45
-rw-r--r--pkg/images/immatch/geoxytran.par28
-rw-r--r--pkg/images/immatch/gregister.cl51
-rw-r--r--pkg/images/immatch/gregister.par33
-rw-r--r--pkg/images/immatch/imalign.cl119
-rw-r--r--pkg/images/immatch/imalign.par28
-rw-r--r--pkg/images/immatch/imcentroid.par16
-rw-r--r--pkg/images/immatch/imcombine.par43
-rw-r--r--pkg/images/immatch/immatch.cl39
-rw-r--r--pkg/images/immatch/immatch.hd32
-rw-r--r--pkg/images/immatch/immatch.men18
-rw-r--r--pkg/images/immatch/immatch.par1
-rw-r--r--pkg/images/immatch/linmatch.par30
-rw-r--r--pkg/images/immatch/mkpkg5
-rw-r--r--pkg/images/immatch/psfmatch.par40
-rw-r--r--pkg/images/immatch/skymap.cl114
-rw-r--r--pkg/images/immatch/skyxymatch.par26
-rw-r--r--pkg/images/immatch/src/geometry/geofunc.gx250
-rw-r--r--pkg/images/immatch/src/geometry/geofunc.x340
-rw-r--r--pkg/images/immatch/src/geometry/geotimtran.x543
-rw-r--r--pkg/images/immatch/src/geometry/geotran.h52
-rw-r--r--pkg/images/immatch/src/geometry/geotran.x1752
-rw-r--r--pkg/images/immatch/src/geometry/geoxytran.gx327
-rw-r--r--pkg/images/immatch/src/geometry/geoxytran.x446
-rw-r--r--pkg/images/immatch/src/geometry/mkpkg34
-rw-r--r--pkg/images/immatch/src/geometry/t_geomap.gx921
-rw-r--r--pkg/images/immatch/src/geometry/t_geomap.x1509
-rw-r--r--pkg/images/immatch/src/geometry/t_geotran.x880
-rw-r--r--pkg/images/immatch/src/geometry/t_geoxytran.x343
-rw-r--r--pkg/images/immatch/src/geometry/trinvert.x163
-rw-r--r--pkg/images/immatch/src/imcombine/imcombine.par43
-rw-r--r--pkg/images/immatch/src/imcombine/mkpkg20
-rw-r--r--pkg/images/immatch/src/imcombine/src/Revisions36
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icaclip.x2207
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icaverage.x424
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/iccclip.x1791
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icgdata.x1531
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icgrow.x263
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icmedian.x753
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icmm.x645
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icnmodel.x528
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icomb.x2198
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icpclip.x879
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icquad.x476
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icsclip.x1923
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icsigma.x434
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icsort.x1096
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/icstat.x892
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/mkpkg27
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/xtimmap.com9
-rw-r--r--pkg/images/immatch/src/imcombine/src/generic/xtimmap.x1207
-rw-r--r--pkg/images/immatch/src/imcombine/src/icaclip.gx575
-rw-r--r--pkg/images/immatch/src/imcombine/src/icaverage.gx120
-rw-r--r--pkg/images/immatch/src/imcombine/src/iccclip.gx471
-rw-r--r--pkg/images/immatch/src/imcombine/src/icemask.x115
-rw-r--r--pkg/images/immatch/src/imcombine/src/icgdata.gx396
-rw-r--r--pkg/images/immatch/src/imcombine/src/icgrow.gx135
-rw-r--r--pkg/images/immatch/src/imcombine/src/icgscale.x88
-rw-r--r--pkg/images/immatch/src/imcombine/src/ichdr.x72
-rw-r--r--pkg/images/immatch/src/imcombine/src/icimstack.x186
-rw-r--r--pkg/images/immatch/src/imcombine/src/iclog.x431
-rw-r--r--pkg/images/immatch/src/imcombine/src/icmask.com8
-rw-r--r--pkg/images/immatch/src/imcombine/src/icmask.h12
-rw-r--r--pkg/images/immatch/src/imcombine/src/icmask.x685
-rw-r--r--pkg/images/immatch/src/imcombine/src/icmedian.gx246
-rw-r--r--pkg/images/immatch/src/imcombine/src/icmm.gx189
-rw-r--r--pkg/images/immatch/src/imcombine/src/icnmodel.gx147
-rw-r--r--pkg/images/immatch/src/imcombine/src/icomb.gx761
-rw-r--r--pkg/images/immatch/src/imcombine/src/icombine.com45
-rw-r--r--pkg/images/immatch/src/imcombine/src/icombine.h63
-rw-r--r--pkg/images/immatch/src/imcombine/src/icombine.x520
-rw-r--r--pkg/images/immatch/src/imcombine/src/icpclip.gx233
-rw-r--r--pkg/images/immatch/src/imcombine/src/icpmmap.x34
-rw-r--r--pkg/images/immatch/src/imcombine/src/icquad.gx133
-rw-r--r--pkg/images/immatch/src/imcombine/src/icrmasks.x41
-rw-r--r--pkg/images/immatch/src/imcombine/src/icscale.x351
-rw-r--r--pkg/images/immatch/src/imcombine/src/icsclip.gx504
-rw-r--r--pkg/images/immatch/src/imcombine/src/icsection.x94
-rw-r--r--pkg/images/immatch/src/imcombine/src/icsetout.x332
-rw-r--r--pkg/images/immatch/src/imcombine/src/icsigma.gx122
-rw-r--r--pkg/images/immatch/src/imcombine/src/icsort.gx386
-rw-r--r--pkg/images/immatch/src/imcombine/src/icstat.gx238
-rw-r--r--pkg/images/immatch/src/imcombine/src/mkpkg67
-rw-r--r--pkg/images/immatch/src/imcombine/src/tymax.x27
-rw-r--r--pkg/images/immatch/src/imcombine/src/xtimmap.gx634
-rw-r--r--pkg/images/immatch/src/imcombine/src/xtprocid.x38
-rw-r--r--pkg/images/immatch/src/imcombine/t_imcombine.x230
-rw-r--r--pkg/images/immatch/src/imcombine/x_imcombine.x1
-rw-r--r--pkg/images/immatch/src/linmatch/linmatch.h298
-rw-r--r--pkg/images/immatch/src/linmatch/linmatch.key51
-rw-r--r--pkg/images/immatch/src/linmatch/lsqfit.h18
-rw-r--r--pkg/images/immatch/src/linmatch/mkpkg21
-rw-r--r--pkg/images/immatch/src/linmatch/rglcolon.x564
-rw-r--r--pkg/images/immatch/src/linmatch/rgldbio.x225
-rw-r--r--pkg/images/immatch/src/linmatch/rgldelete.x993
-rw-r--r--pkg/images/immatch/src/linmatch/rgliscale.x593
-rw-r--r--pkg/images/immatch/src/linmatch/rglpars.x104
-rw-r--r--pkg/images/immatch/src/linmatch/rglplot.x1592
-rw-r--r--pkg/images/immatch/src/linmatch/rglregions.x1084
-rw-r--r--pkg/images/immatch/src/linmatch/rglscale.x1337
-rw-r--r--pkg/images/immatch/src/linmatch/rglshow.x107
-rw-r--r--pkg/images/immatch/src/linmatch/rglsqfit.x443
-rw-r--r--pkg/images/immatch/src/linmatch/rgltools.x1017
-rw-r--r--pkg/images/immatch/src/linmatch/t_linmatch.x544
-rw-r--r--pkg/images/immatch/src/listmatch/mkpkg12
-rw-r--r--pkg/images/immatch/src/listmatch/t_imctroid.x1016
-rw-r--r--pkg/images/immatch/src/listmatch/t_xyxymatch.x406
-rw-r--r--pkg/images/immatch/src/mkpkg11
-rw-r--r--pkg/images/immatch/src/psfmatch/mkpkg21
-rw-r--r--pkg/images/immatch/src/psfmatch/psfmatch.h274
-rw-r--r--pkg/images/immatch/src/psfmatch/psfmatch.key50
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpbckgrd.x70
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpcolon.x501
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpconvolve.x106
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpfft.x443
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpfilter.x502
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpisfm.x556
-rw-r--r--pkg/images/immatch/src/psfmatch/rgppars.x124
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpregions.x464
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpsfm.x815
-rw-r--r--pkg/images/immatch/src/psfmatch/rgpshow.x116
-rw-r--r--pkg/images/immatch/src/psfmatch/rgptools.x641
-rw-r--r--pkg/images/immatch/src/psfmatch/t_psfmatch.x365
-rw-r--r--pkg/images/immatch/src/wcsmatch/mkpkg14
-rw-r--r--pkg/images/immatch/src/wcsmatch/rgmatchio.x77
-rw-r--r--pkg/images/immatch/src/wcsmatch/t_skyxymatch.x690
-rw-r--r--pkg/images/immatch/src/wcsmatch/t_wcscopy.x199
-rw-r--r--pkg/images/immatch/src/wcsmatch/t_wcsxymatch.x787
-rw-r--r--pkg/images/immatch/src/wcsmatch/wcsxymatch.h15
-rw-r--r--pkg/images/immatch/src/xregister/mkpkg25
-rw-r--r--pkg/images/immatch/src/xregister/oxregister.key33
-rw-r--r--pkg/images/immatch/src/xregister/rgxbckgrd.x63
-rw-r--r--pkg/images/immatch/src/xregister/rgxcolon.x508
-rw-r--r--pkg/images/immatch/src/xregister/rgxcorr.x1034
-rw-r--r--pkg/images/immatch/src/xregister/rgxdbio.x290
-rw-r--r--pkg/images/immatch/src/xregister/rgxfft.x179
-rw-r--r--pkg/images/immatch/src/xregister/rgxfit.x814
-rw-r--r--pkg/images/immatch/src/xregister/rgxgpars.x68
-rw-r--r--pkg/images/immatch/src/xregister/rgxicorr.x583
-rw-r--r--pkg/images/immatch/src/xregister/rgximshift.x391
-rw-r--r--pkg/images/immatch/src/xregister/rgxplot.x317
-rw-r--r--pkg/images/immatch/src/xregister/rgxppars.x49
-rw-r--r--pkg/images/immatch/src/xregister/rgxregions.x459
-rw-r--r--pkg/images/immatch/src/xregister/rgxshow.x172
-rw-r--r--pkg/images/immatch/src/xregister/rgxtools.x685
-rw-r--r--pkg/images/immatch/src/xregister/rgxtransform.x446
-rw-r--r--pkg/images/immatch/src/xregister/t_xregister.x440
-rw-r--r--pkg/images/immatch/src/xregister/xregister.h250
-rw-r--r--pkg/images/immatch/src/xregister/xregister.key47
-rw-r--r--pkg/images/immatch/sregister.cl151
-rw-r--r--pkg/images/immatch/wcscopy.par5
-rw-r--r--pkg/images/immatch/wcsmap.cl111
-rw-r--r--pkg/images/immatch/wcsxymatch.par25
-rw-r--r--pkg/images/immatch/wregister.cl148
-rw-r--r--pkg/images/immatch/xregister.par42
-rw-r--r--pkg/images/immatch/xyxymatch.par36
176 files changed, 70576 insertions, 0 deletions
diff --git a/pkg/images/immatch/Revisions b/pkg/images/immatch/Revisions
new file mode 100644
index 00000000..a45cc7be
--- /dev/null
+++ b/pkg/images/immatch/Revisions
@@ -0,0 +1,2025 @@
+.help revisions Jan97 images.immatch
+.nf
+===============================
+Package Reorganization
+===============================
+
+pkg/images/imarith/t_imsum.x
+pkg/images/imarith/t_imcombine.x
+pkg/images/doc/imsum.hlp
+pkg/images/doc/imcombine.hlp
+ Provided options for USHORT data. (12/10/96, Valdes)
+
+pkg/images/imarith/icsetout.x
+pkg/images/doc/imcombine.hlp
+ A new option for computing offsets from the image WCS has been added.
+ (11/30/96, Valdes)
+
+pkg/images/imarith/t_imcombine.x
+pkg/images/imarith/icombine.gx
+ Changed the error checking to catch additional errors relating to too
+ many files. (11/12/96, Valdes)
+
+pkg/images/imarith/icsort.gx
+ There was an error in the ic_2sort routine when there are exactly
+ three images that one of the explicit cases did not properly keep
+ the image identifications. See buglog 344. (8/1/96, Valdes)
+
+pkg/images/filters/median.x
+ The routine mde_yefilter was being called with the wrong number of
+ arguments.
+ (7/18/96, Davis)
+
+pkg/images/imarith/t_imcombine.x
+pkg/images/imarith/icombine.gx
+pkg/images/imarith/icimstack.x +
+pkg/images/imarith/iclog.x
+pkg/images/imarith/mkpkg
+pkg/images/doc/imcombine.hlp
+ The limit on the maximum number of images that can be combined, set by
+ the maximum number of logical file descriptors, has been removed. If
+ the condition of too many files is detected the task now automatically
+ stacks all the images in a temporary image and then combines them with
+ the project option.
+ (5/14/96, Valdes)
+
+pkg/images/geometry/xregister/rgxfit.x
+ Changed several Memr[] references to Memi[] in the rg_fit routine.
+ This bug was causing a floating point error in the xregister task
+ on the Dec Alpha if the coords file was defined, and could potentially
+ cause problems on other machines.
+ (Davis, April 3, 1996)
+
+pkg/images/geometry/t_geotran.x
+pkg/images/geometry/geograph.x
+pkg/images/doc/geomap.hlp
+ Corrected the definition of skew in the routines which compute a geometric
+ interpretation of the 6-coefficient fit, which compute the coefficients
+ from the geometric parameters, and in the relevant help pages.
+ (2/19/96, Davis)
+
+pkg/images/median.par
+pkg/images/rmedian.par
+pkg/images/mode.par
+pkg/images/rmode.par
+pkg/images/fmedian.par
+pkg/images/frmedian.par
+pkg/images/fmode.par
+pkg/images/frmode.par
+pkg/images/doc/median.hlp
+pkg/images/doc/rmedian.hlp
+pkg/images/doc/mode.hlp
+pkg/images/doc/rmode.hlp
+pkg/images/doc/fmedian.hlp
+pkg/images/doc/frmedian.hlp
+pkg/images/doc/fmode.hlp
+pkg/images/doc/frmode.hlp
+pkg/images/filters/t_median.x
+pkg/images/filters/t_rmedian.x
+pkg/images/filters/t_mode.x
+pkg/images/filters/t_rmode.x
+pkg/images/filters/t_fmedian.x
+pkg/images/filters/t_frmedian.x
+pkg/images/filters/t_fmode.x
+pkg/images/filters/t_frmode.x
+ Added a verbose parameter to the median, rmedian, mode, rmode, fmedian,
+ frmedian, fmode, and frmode tasks. (11/27/95, Davis)
+
+pkg/images/geometry/doc/geotran.hlp
+ Fixed an error in the help page for geotran. The default values for
+ the xscale and yscale parameters were incorrectly listed as INDEF,
+ INDEF instead of 1.0, 1.0. (11/14/95, Davis)
+
+pkg/images/imarith/icpclip.gx
+ Fixed a bug where a variable was improperly used for two different
+ purposes causing the algorithm to fail (bug 316). (10/19/95, Valdes)
+
+pkg/images/doc/imcombine.hlp
+ Clarified a point about how the sigma is calculated with the SIGCLIP
+ option. (10/11/95, Valdes)
+
+pkg/images/imarith/icombine.gx
+ To deal with the case of readnoise=0. and image data which has points with
+ negative mean or median and very small minimum readnoise is set
+ internally to avoid computing a zero sigma and dividing by it. This
+ applies to the noise model rejection options. (8/11/95, Valdes)
+
+pkg/images/frmedian.hlp
+pkg/images/frmode.hlp
+pkg/images/rmedian.hlp
+pkg/images/rmode.hlp
+pkg/images/frmedian.par
+pkg/images/frmode.par
+pkg/images/rmedian.par
+pkg/images/rmode.par
+pkg/images/filters/frmedian.h
+pkg/images/filters/frmode.h
+pkg/images/filters/rmedian.h
+pkg/images/filters/rmode.h
+pkg/images/filters/t_frmedian.x
+pkg/images/filters/t_frmode.x
+pkg/images/filters/t_rmedian.x
+pkg/images/filters/t_rmode.x
+pkg/images/filters/frmedian.x
+pkg/images/filters/frmode.x
+pkg/images/filters/rmedian.x
+pkg/images/filters/rmode.x
+pkg/images/filters/med_utils.x
+ Added new ring median and modal filtering tasks frmedian, rmedian,
+ frmode, and rmode to the images package.
+ (6/20/95, Davis)
+
+pkg/images/fmedian.hlp
+pkg/images/fmode.hlp
+pkg/images/median.hlp
+pkg/images/mode.hlp
+pkg/images/fmedian.par
+pkg/images/fmode.par
+pkg/images/median.par
+pkg/images/mode.par
+pkg/images/filters/fmedian.h
+pkg/images/filters/fmode.h
+pkg/images/filters/median.h
+pkg/images/filters/mode.h
+pkg/images/filters/t_fmedian.x
+pkg/images/filters/t_fmode.x
+pkg/images/filters/t_median.x
+pkg/images/filters/t_mode.x
+pkg/images/filters/fmedian.x
+pkg/images/filters/fmode.x
+pkg/images/filters/median.x
+pkg/images/filters/mode.x
+pkg/images/filters/fmd_buf.x
+pkg/images/filters/fmd_hist.x
+pkg/images/filters/fmd_maxmin.x
+pkg/images/filters/med_buf.x
+pkg/images/filters/med_sort.x
+ Added minimum and maximum good data parameters to the fmedian, fmode,
+ median, and mode filtering tasks. Removed the 64X64 kernel size limit
+ in the median and mode tasks. Replaced the common blocks with structures
+ and .h files.
+ (6/20/95, Davis)
+
+pkg/images/geometry/t_geotran.x
+pkg/images/geometry/geotran.x
+pkg/images/geometry/geotimtran.x
+ Fixed a bug in the buffering of the x and y coordinate surface interpolants
+ which can cause a memory corruption error if, nthe nxsample or nysample
+ parameters are > 1, and the nxblock or nyblock parameters are less
+ than the x and y dimensions of the input image. Took the opportunity
+ to clean up the code.
+ (6/13/95, Davis)
+
+=======
+V2.10.4
+=======
+
+pkg/images/geometry/t_geomap.x
+ Corrected a harmless typo in the code which determines the minimum
+ and maximum x values and improved the precision of the test when the
+ input is double precision.
+ (4/18/95, Davis)
+
+pkg/images/doc/fit1d.hlp
+ Added a description of the interactive parameter to the fit1d help page.
+ (4/17/95, Davis)
+
+pkg/images/imarith/t_imcombine.x
+ If an error occurs while opening an input image header the error
+ recovery will close all open images and then propagate the error.
+ For the case of running out of file descriptors with STF format
+ images this will allow the error message to be printed rather
+ than the error code. (4/3/95, Valdes)
+
+pkg/images/geometry/xregister/t_xregister.x
+ Added a test on the status code returned from the fitting routine so
+ the xregister tasks does not go ahead and write an output image when
+ the user quits the task in in interactive mode.
+ (3/31/95, Davis)
+
+pkg/images/imarith/icscale.x
+pkg/images/doc/imcombine.hlp
+ The behavior of the weights when using both multiplicative and zero
+ point scaling was incorrect; the zero levels have to account for
+ the scaling. (3/27/95, Valdes)
+
+pkg/images/geometry/xregister/rgxtools.x
+ Changed some amovr and amovi calls to amovkr and amovki calls.
+ (3/15/95, Davis)
+
+pkg/images/geometry/t_imshift.x
+pkg/images/geometry/t_magnify.x
+pkg/images/geometry/geotran.x
+pkg/images/geometry/xregister/rgximshift.x
+ The buffering margins set for the bicubic spline interpolants were
+ increased to improve the flux conservation properties of the interpolant
+ in cases where the data is undersampled. (12/6/94, Davis)
+
+pkg/images/xregister/rgxbckgrd.x
+ In several places the construct array[1++nx-wborder] was being used
+ instead of array[1+nx-wborder]. Apparently caused by a typo which
+ propagated through the code, the Sun compilers did not catch this, but
+ the IBM/RISC6000 compilers did. (11/16/94, Davis)
+
+
+pkg/images/xregister.par
+pkg/images/doc/xregister.hlp
+pkg/images/geometry/xregister/t_xregister.x
+pkg/images/geometry/xregister/rgxcorr.x
+pkg/images/geometry/xregister/rgxicorr.x
+pkg/images/geometry/xregister/rgxcolon.x
+pkg/images/geometry/xregister/rgxdbio.x
+ The xregister task was modified to to write the output shifts file
+ in either text database format (the current default) or in simple text
+ format. The change was made so that the output of xregister could
+ both be edited more easily by the user and be used directly with the
+ imshift task. (11/11/94, Davis)
+
+pkg/images/imfit/fit1d.x
+ A Memc in the ratio output option was incorrectly used instead of Memr
+ when the bug fix of 11/16/93 was made. (10/14/94, Valdes)
+
+pkg/images/geometry/xregister/rgxcorr.x
+ The procedure rg_xlaplace was being incorrectly declared as an integer
+ procedure.
+ (8/1/94, Davis)
+
+pkg/images/geometry/xregister/rgxregions.x
+ The routine strncmp was being called (with a missing number of characters
+ argument) instead of strcmp. This was causing a bus error under solaris
+ but not sun os whenever the user set regions to "grid ...". (7/27/94 LED)
+
+pkg/images/tv/imexaine/ierimexam.x
+ The Gaussian fitting can return a negative sigma**2 which would cause
+ an FPE when the square root is taken. This will only occur when
+ there is no reasonable signal. The results of the gaussian fitting
+ are now set to INDEF if this unphysical result occurs. (7/7/94, Valdes)
+
+pkg/images/geometry/geofit.x
+ A routine expecting two char arrays was being passed two real arrays
+ instead resulting in a segmentation violation if calctype=real
+ and reject > 0.
+ (6/21/94, Davis)
+
+pkg/images/imarith/t_imarith.x
+ IMARITH now deletes the CCDMEAN keyword if present. (6/21/94, Valdes)
+
+pkg/images/imarith/icaclip.gx
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/icpclip.gx
+pkg/images/imarith/icsclip.gx
+ 1. The restoration of deleted pixels to satisfy the nkeep parameter
+ was being done inside the iteration loop causing the possiblity
+ of a non-terminating loop; i.e. pixels are rejected, they are
+ restored, and the number left then does not statisfy the termination
+ condition. The restoration step was moved following the iterative
+ rejection.
+ 2. The restoration was also incorrectly when mclip=no and could
+ lead to a segmentation violation.
+ (6/13/94, Valdes)
+
+pkg/images/geometry/xregister/rgxicorr.x
+ The path names to the xregister task interactive help files was incorrect.
+ (6/13/94, Davis)
+
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/icsclip.gx
+ Found and fixed another typo bug. (6/7/94, Valdes/Zhang)
+
+pkg/images/imarith/icscale.x
+ The sigma scaling flag, doscale1, would not be set in the case of
+ a mean offset of zero though the scale factors could be different.
+ (5/25/94, Valdes/Zhang)
+
+pkg/images/imarith/icsclip.gx
+ There was a missing line: l = Memi[mp1]. (5/25/94, Valdes/Zhang)
+
+pkg/images/imarith/icaclip.gx
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/icpclip.gx
+pkg/images/imarith/icsclip.gx
+ The reordering step when a central median is used during rejection
+ but the final combining is average was incorrect if the number
+ of rejected low pixels was greater than the number of pixel
+ number of pixels not rejected. (5/25/94, Valdes)
+
+pkg/images/geometry/t_geotran.x
+ In cases where there was no input geomap database, geotran was
+ unnecessarily overiding the size of the input image requested by the
+ user if the size of the image was bigger than the default output size
+ (the size of the output image which would include all the input image
+ pixels is no user shifts were applied).
+ (5/10/94, Davis)
+
+pkg/images/imarith/icscale.x
+pkg/images/imarith/t_imcombine.x
+ 1. There is now a warning error if the scale, zero, or weight type
+ is unknown.
+ 2. An sfree was being called before the allocated memory was finished
+ being used.
+ (5/2/94, Valdes)
+
+pkg/images/tv/imexaine/ierimexam.x
+ For some objects the moment analysis could fail producing a floating
+ overflow error in imexamine, because the code was trying to use
+ INDEF as the initial value of the object fwhm. Changed the gaussian
+ fitting code to use a fraction of the fitting radius as the initial value
+ for the fitted full-width half-maximum in cases where the moment analysis
+ cannot compute an initial value.
+ (4/15/94 LED)
+
+pkg/images/imarith/iclog.x
+ Changed the mean, median, mode, and zero formats from 6g to 7.5g to
+ insure 5 significant digits regardless of signs and decimal points.
+ (4/13/94, Valdes)
+
+pkg/images/doc/imcombine.hlp
+ Tried again to clarify the scaling as multiplicative and the offseting
+ as additive for file input and for log output. (3/22/94, Valdes)
+
+pkg/images/imarith/iacclip.gx
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/iscclip.gx
+ The image sigma was incorrectly computed when an offset scaling is used.
+ (3/8/94, Valdes)
+
+pkg/images/doc/imcombine.hlp
+ The MINMAX example confused low and high. (3/7/94, Valdes)
+
+pkg/images/geometry/t_geomap.x
+pkg/images/geometry/geofit.x
+pkg/images/geometry/geograph.x
+ Fixed a bug in the geomap code which caused the linear portion of the transformation
+ to be computed incorrectly if the x and y fits had a different functional form.
+ (12/29/93, Davis)
+
+pkg/images/imarith/t_imcombine.x
+pkg/images/imcombine.par
+pkg/images/do/imcombine.hlp
+ The output pixel datatypes now include unsigned short integer.
+ (12/4/93, Valdes)
+
+pkg/images/doc/imcombine.hlp
+ Fixed an error in the example of offseting. (11/23/93, Valdes)
+
+pkg/images/imfit/fit1d.x
+ When doing operations in place the input and output buffers are the
+ same and the difference and ratio operations assumed they were not
+ causing the final results to be wrong. (11/16/93, Valdes)
+
+pkg/images/imarith/t_imarith.x
+pkg/images/doc/imarith.hlp
+ If no calculation type is specified then it will be at least real
+ for a division. Since the output pixel type defaults to the
+ calculation type if not specified this will also result in a
+ real output if dividing two integer images. (11/12/93, Valdes)
+
+pkg/images/imarith/icgrow.gx
+pkg/images/imarith/icpclip.gx
+pkg/images/imarith/icsclip.gx
+pkg/images/imarith/icaclip.gx
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/t_imcombine.x
+pkg/images/doc/imcombine.hlp
+ If there were fewer initial pixels than specified by nkeep then the
+ task would attempt to add garbage data to achieve nkeep pixels. This
+ could occur when using offsets, bad pixel masks, or thresholds. The
+ code was changed to check against the initial number of pixels rather
+ than the number of images. Also a negative nkeep is no longer
+ converted to a positive value based on the number of images. Instead
+ it specifies the maximum number of pixels to reject from the initial
+ set of pixels. (11/8/93, Valdes)
+
+=======
+V2.10.2
+=======
+
+pkg/images/imarith/icsetout.x
+ Added MWCS calls to update the axis mapping when using the project
+ option in IMCOMBINE. (10/8/93, Valdes)
+
+pkg$images/imarith/icscale.x
+pkg$images/doc/imcombine.hlp
+ The help indicated that user input scale or zero level factors
+ by an @file or keyword are multiplicative and additive while the
+ task was using then as divisive and subtractive. This was
+ corrected to agree with the intend of the documentation.
+ Also the factors are no longer normalized. (9/24/93, Valdes)
+
+pkg$images/imarith/icsetout.x
+ The case in which absolute offsets are specified but the offsets are
+ all the same did not work correctly. (9/24/93, Valdes)
+
+pkg$images/imfit/imsurfit.h
+pkg$images/imfit/t_imsurfit.x
+pkg$images/imfit/imsurfit.x
+pkg$images/lib/ranges.x
+ Fixed two bugs in the imsurfit task bad pixel rejection code. For low
+ k-sigma rejections factors the bad pixel list could overflow resulting
+ in a segmentation violation or a hung task. Overlapping ranges were
+ not being decoded into a bad pixel list properly resulting in
+ oscillating bad pixel rejection behavior where certain groups of
+ bad pixels were alternately being included and excluded from the fit.
+ Both bugs are fixed in iraf 2.10.3
+ (9/21/93, Davis)
+
+pkg$images/doc/imcombine.hlp
+ Clarified how bad pixel masks work with the "project" option.
+ (9/13/93, Valdes)
+
+pkg$images/imfit/fit1d.x
+ When the input and output images are the same there was an typo error
+ such that the output was opened separately but then never unmapped
+ resulting in the end of the image not being updated. (8/6/93, Valdes)
+
+pkg$images/imarith/t_imcombine.x
+ The algorithm for making sure there are enough file descriptors failed
+ to account for the need to reopen the output image header for an
+ update. Thus when the number of input images + output images + logfile
+ was exactly 60 the task would fail. The update occurs when the output
+ image is unmapped so the solution was to close the input images first
+ except for the first image whose pointer is used in the new copy of the
+ output image. (8/4/93, Valdes)
+
+pkg$images/filters/t_mode.x
+pkg$images/filters/t_median.x
+ Fixed a bug in the error trapping code in the median and mode tasks.
+ The call to eprintf contained an extra invalid error code agument.
+ (7/28/93, Davis)
+
+pkg$images/geometry/geomap.par
+pkg$images/geometry/t_geomap.x
+pkg$images/geometry/geogmap.x
+pkg$images/geometry/geofit.x
+ Fixed a bug in the error handling code in geomap which was producing
+ a segmentation violation on exit if the user's coordinate list
+ had fewer than 3 data points. Also improved the error messages
+ presented to the user in both interactive and non-interactive mode.
+ (7/7/93, Davis)
+
+pkg$images/imarith/icgdata.gx
+ There was an indexing error in setting up the ID array when using
+ the grow option. This caused the CRREJECT/CCDCLIP algorithm to
+ fail with a floating divide by zero error when there were non-zero
+ shifts. (5/26/93, Valdes)
+
+pkg$images/imarith/icmedian.gx
+ The median calculation is now done so that the original input data
+ is not lost. This slightly greater inefficiency is required so
+ that an output sigma image may be computed if desired. (5/10/93, Valdes)
+
+pkg$images/geometry/t_imshift.x
+ Added support for type ushort to the imshift task in cases where the
+ pixel shifts are integral.
+ (5/8/93, Davis)
+
+pkg$images/doc/rotate.hlp
+ Fixed a bug in the rotate task help page which implied that automatic
+ image size computation would occur if ncols or nlines were set no 0
+ instead of ncols and nlines.
+ (4/17/93, Davis)
+
+pkg$images/imarith/imcombine.gx
+ There was no error checking when writing to the output image. If
+ an error occurred (the example being when an imaccessible imdir was
+ set) obscure messages would result. Errchks were added.
+ (4/16/93, Valdes)
+
+pkg$images/doc/gauss.hlp
+ Fixed 2 sign errors in the equations in the documentation describing
+ the elliptical gaussian fucntion.
+ (4/13/92, Davis)
+
+pkg/images/imutil/t_imslice.x
+ Removed an error check in the imslice task, which was preventing it from
+ being used to reduce the dimensionality of images where the length of
+ the slice dimension is 1.0.
+ (2/16/83, Davis)
+
+pkg/images/filters/fmedian.x
+ The fmedian task was printing debugging information under iraf 2.10.2.
+ (1/25/93, Davis)
+
+pkg/images/imarith/icaclip.gx
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/icpclip.gx
+pkg/images/imarith/icsclip.gx
+ When using mclip=yes and when more pixels are rejected than allowed by
+ the nkeep parameter there was a subtle bug in how the pixels are added
+ back which can result in a segmentation violation.
+ if (nh == n2) ==> if (nh == n[i])
+ (1/20/93, Valdes)
+
+
+=======
+V2.10.1
+=======
+
+pkg/images/imarith/t_imcombine.x
+pkg/images/imarith/icaclip.gx
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/icgrow.gx
+pkg/images/imarith/iclog.x
+pkg/images/imarith/icombine.com
+pkg/images/imarith/icombine.gx
+pkg/images/imarith/icombine.h
+pkg/images/imarith/icpclip.gx
+pkg/images/imarith/icscale.x
+pkg/images/imarith/icsclip.gx
+pkg/images/imarith/icsetout.x
+pkg/images/imcombine.par
+pkg/images/doc/combine.hlp
+ The weighting was changed from using the square root of the exposure time
+ or image statistics to using the values directly. This corresponds
+ to variance weighting. Other options for specifying the scaling and
+ weighting factors were added; namely from a file or from a different
+ image header keyword. The \fInkeep\fR parameter was added to allow
+ controlling the maximum number of pixels to be rejected by the clipping
+ algorithms. The \fIsnoise\fR parameter was added to include a sensitivity
+ or scale noise component to the noise model. Errors will now delete
+ the output image.
+ (9/30/92, Valdes)
+
+pkg/images/imutil/imcopy.x
+ Added a call to flush after the status line printout so that the output
+ will appear immediately. (8/19/92, Davis)
+
+pkg/images/filters/mkpkg
+pkg/images/filters/t_fmedian.x
+pkg/images/filters/fmedian.x
+pkg/images/filters/fmd_buf.x
+pkg/images/filters/fmd_maxmin.x
+ The fmedian task could crash with a segmentation violation if mapping
+ was turned off (hmin = zmin and hmax = zmax) and the input image
+ contained data outside the range defined by zmin and zmax. (8/18/92, Davis)
+
+pkg/images/imarith/icaclip.gx
+pkg/images/imarith/iccclip.gx
+pkg/images/imarith/icpclip.gx
+pkg/images/imarith/icsclip.gx
+ There was a very unlikely possibility that if all the input pixels had
+ exactly the same number of rejected pixels the weighted average would
+ be done incorrectly because the dflag would not be set. (8/11/92, Valdes)
+
+pkg/images/imarith/icmm.gx
+ This procedure failed to set the dflag resulting in the weighted average
+ being computed in correctly. (8/11/92, Valdes)
+
+pkg/images/imfit/fit1d.x
+ At some point changes were made but not documented dealing with image
+ sections on the input/output. The changes seem to have left off the
+ final step of opening the output image using the appropriate image
+ sections. Because of this it is an error to use an image section
+ on an input image when the output image is different; i.e.
+
+ cl> fit1d dev$pix[200:400,*] junk
+
+ This has now been fixed. (8/10/92, Valdes)
+
+pkg/images/imarith/icscales.x
+ The zero levels were incorrectly scaled twice. (8/10/92, Valdes)
+
+pkg/images/imarith/icstat.gx
+ Contained the statement
+ nv = max (1., (Memi[v2+i] - Memi[v1+i]) / Memi[dv+i] + 1)
+ which is max(real,int). Changed the 1. to a 1. (8/10/92, Valdes)
+
+pkg$images/imarith/icaclip.gx
+pkg$images/imarith/iccclip.gx
+pkg$images/imarith/icsclip.gx
+ These files contained multiple cases (ten or so) of constructs such as
+ "max (1., ...)" or "max (0., ...)" where the ... could be either real
+ or double. In the double cases the DEC compiler complained about a
+ type mismatch since 1. is real. (8/10/92, Valdes)
+
+pkg$images/imfit/t_imsurfit.x
+ Fixed a bug in the section reading code. Imsurfit is supposed to switch
+ the order of the section delimiters in x and y if x2 < x1 or y2 < 1.
+ Unfortunately the y test was actually "if (y2 < x1)" instead of
+ "if (y2 < y1)". Whether or not the code actually works correctly
+ depends on the value of x1 relative to y2. This bug was not present
+ in 2.9.1 but is present in subsequent releases. (7/30/92 LED)
+
+=======
+V2.10.1
+=======
+
+pkg$images/filters/t_gauss.x
+ The case theta=90 and ratio > 0.0 but < 1.0 was producing an incorrect
+ convolution if bilinear=yes, because the major axis sigmas being
+ input along the x and y axes were sigma and ratio * sigma respectively
+ instead of ratio * sigma and sigma in this case.
+
+pkg$images/imutil/imcopy.x
+ Modified imcopy to write its verbose output to STDOUT instead of
+ STDERR. (6/24/92, Davis)
+
+pkg$images/imarith/imcombine.gx
+ The step where impl1$t is called to check if there is enough memory
+ did not set the return buffer because the values are irrelevant for
+ this check. However, depending on history, this buffer could have
+ arbitrary values and later when IMIO attempts to flush this buffer,
+ at least in the case of image type coersion, cause arithmetic errors.
+ The fix was to clear the returned buffers. (4/27/92, Valdes)
+
+pkg$images/imutil/t_imstack.x
+ Modified the imslice task to read the old and write a new axis map.
+ (4/23/92, Davis)
+
+pkg$images/geometry/t_imslice.x
+ Modified the imslice task to read the old and write a new axis map.
+ (4/23/92, Davis)
+
+pkg$images/geometry/t_blkavg.x
+pkg$images/geometry/t_blkrep.x
+ Modified the calls to mw_shift and mw_scale to explicitly set the
+ number of logical axes instead of using the default of 0.
+ (4/23/92, Davis)
+
+pkg$images/geometry/t_imtrans.x
+ Modified imtranspose so that it correctly picks up the axis map
+ and writes it to the output image wcs. (4/23/92, Davis)
+
+pkg$images/register.par
+pkg$images/geotran.par
+pkg$images/doc/register.hlp
+pkg$images/doc/geotran.hlp
+ Changed the default values of the parameters xscale and yscale in
+ the register and geotran tasks from INDEF to 1.0 (4/23/92, Davis)
+
+pkg$images/geometry/t_imtrans.x
+pkg$images/doc/imtranspose.hlp
+ Modified the imtranspose task so it does a true transpose of the
+ axes instead of simply modifying the lterm. (4/8/92, Davis)
+
+pkg$images/iminfo/listpixels.x
+ Added the formats parameter for formatting the output pixel coordinates
+ to the listpixels task. These formats take precedence over the formats
+ stored in the WCS in the image header and the previous default format.
+ (4/7/92, Davis)
+
+pkg$images/imutil/t_imstack.x
+ Added wcs support to the imstack task. (4/2/92, Davis)
+
+pkg$images/iminfo/listpixels.x
+ Modified listpixels so that it will work correctly if the dimension
+ of the wcs is less than the dimension of the image. (3/16/92, Davis)
+
+pkg$images/geometry/t_geotran.x
+ Modified the rotate, imlintran, register and geotran tasks wcs updating
+ code to deal correclty with dimensionally reduced data. (3/16/92, Davis)
+
+pkg$images/imarith/icalip.gx
+pkg$images/imarith/icclip.gx
+pkg$images/imarith/ipslip.gx
+pkg$images/imarith/icslip.gx
+pkg$images/imarith/icmedian.gx
+ The median calculation with an even number of points for short data
+ could overflow (addition of two short values) and be incorrect.
+ (3/16/92, Valdes)
+
+pkg$images/geometry/t_blkavg.x
+pkg$images/geometry/t_blkrep.x
+ 1. Improved the precision of the blkavg task wcs updating code.
+ 2. Changed the blkrep task wcs updating code so that it is consistent
+ with blkavg. This means that a blkrep command followed by a blkavg
+ command or vice versa will return the original coordinate system
+ to within machine precision. (3/16/92, Davis)
+
+pkg$images/iminfo/listpixels.x
+ Modified listpixels to print out an error if it could not open the
+ wcs in the image. (3/15/92, Davis)
+
+pkg$images/geometry/t_magnify.x
+ Fixed a bug in the magnify task wcs updating code which was not
+ working correctly for dimensionally reduced images. (3/15/92, Davis)
+
+pkg$images/geometry/t_imtrans.x
+ Fixed a bug in the imtranspose task wcs updating code which was not
+ working correctly for dimensionally reduced images. (3/14/92, Davis)
+
+pkg$images/imarith/icalip.gx
+pkg$images/imarith/icclip.gx
+pkg$images/imarith/icslip.gx
+ There was a bug allowing the number of valid pixels counter to become
+ negative. Also there was a step which should not be done if the
+ number of valid pixels is less than 1; i.e. all pixels rejected.
+ A test was put in to skip this step. (3/13/92, Valdes)
+
+pkg$images/iminfo/t_imslice.x
+pkg$images/doc/imslice.hlp
+ Added wcs support to the imslice task.
+ (3/12/92, Davis)
+
+pkg$images/iminfo/t_imstat.x
+ Fixed a bug in the code for computing the standard deviation, kurtosis,
+ and skew, wherein precision was being lost because two of the intermediate
+ variables in the computation were real instead of double precision.
+ (3/10/92, Davis)
+
+pkg$images/iminfo/listpixels.x
+ 1. Modified listpixels task to use the MWCS axis "format" attributes
+ if they are present in the image header.
+ 2. Added support for dimensionally reduced images, i.e.
+ images which are sections of larger images and whose coordinate
+ transformations depend on the reduced axes, to the listpixels task.
+ (3/6/92, Davis)
+
+pkg$images/imarith/t_imcombine.x
+pkg$images/imarith/icsetout.x
+ Changed error messages to say IMCOMBINE instead of ICOMBINE.
+ (3/2/92, Valdes)
+
+pkg$images/imarith/iclog.x
+ Added listing of read noise and gain. (2/10/92, Valdes)
+
+pkg$images/imarith/icscale.x
+pkg$images/imarith/icpclip.gx
+ 1. Datatype declaration for asumi was incorrect.
+ 2. Reduced the minimum number of images allowed for PCLIP to 3.
+ (1/7/92, Valdes)
+
+pkg$images/imarith/icgrow.gx
+ The first pixel to be checked was incorrectly set to 0 instead of 1
+ resulting in a segvio when using the grow option. (12/6/91, Valdes)
+
+pkg$images/imarith/icgdata.gx
+pkg$images/imarith/icscale.x
+ Fixed datatype declaration errors found by SPPLINT. (11/22/91, Valdes)
+
+pkg$images/iminfo/t_imstat.x
+ Fixed a bug in the kurtosis computation found by ST.
+ (Davis 10/11/91)
+
+pkg$images/iminfo/t_imstat.x
+pkg$images/doc/imstat.hlp
+ Corrected a bug in the mode computation in imstatistics. The parabolic
+ interpolation correction for computing the histogram peak was being
+ applied in the wrong direction. Note that for dev$pix the wrong answer
+ is actually closer to the expected answer than the correct answer
+ due to binning effects.
+ (Davis 9/24/91)
+
+pkg$images/filters/t_gauss.x
+ The code which computes the gaussian kernel was producing a divide by
+ zero error if ratio=0.0 and bilinear=yes (2.10 version only).
+ (Davis 9/18/91)
+
+pkg$images/doc/magnify.hlp
+ Corrected a bug in the magnify help page.
+ (Davis 9/18/91)
+
+pkg$images/imarith/icsclip.gx
+pkg$images/imarith/icaclip.gx
+pkg$images/imarith/iccclip.gx
+ There was a typo, Memr[d[k]+k] --> Memr[d[j]+k]. (9/17/91, Valdes)
+
+pkg$images/imarith/icstat.gx
+pkg$images/imarith/icmask.x
+ The offsets were used improperly in computing image statistics.
+ (Valdes, 9/17/91)
+
+pkg$images/geometry/t_imshift.x
+ The shifts file pointer was not being correctly initialized to NULL
+ in the case where no shifts file was declared. When the task
+ was invoked repeatedly from a script, this could result in an array being
+ referenced, for which space had not been previously allocated.
+ (Davis 7/29/91)
+
+pkg$images/imarith/imc* -
+pkg$images/imarith/ic* +
+pkg$images/imarith/t_imcombine.x
+pkg$images/imarith/mkpkg
+pkg$images/imarith/generic/mkpkg
+pkg$images/imcombine.par
+pkg$images/doc/imcombine.hlp
+ Replaced old version of IMCOMBINE with new version supporting masks,
+ offsets, and new algorithms. (Valdes 7/19/91)
+
+pkg$images/iminfo/imhistogram.x
+ Imhistogram has been modified to print the value of the middle of
+ histogram bin instead of the left edge if the output type is list
+ instead of plot. (Davis 6/11/91)
+
+pkg$images/t_imsurfit.x
+ Modified the sections file reading code to check the order of the
+ x1 x2 y1 y2 parameters and switch (x1,x2) or (y1,y2) if x2 < x1 or
+ y2 < y1 respectively. (Davis 5/28/91)
+
+pkg$images/listpixels.par
+pkg$images/iminfo/listpixels.x
+pkg$images/doc/listpixels.hlp
+ Modified the listpixels task to be able to print the pixel coordinates
+ in logical, physical or world coordinates. The default coordinate
+ system is still logical as before. (Davis 5/17/91)
+
+pkg$images/images.par
+pkg$images/doc/minmax.hlp
+pkg$images/imutil/t_minmax.x
+pkg$images/imutil/minmax.x
+ Minmax was modified to do the minimum and maximum values computations
+ in double precision or complex instead of real if the input image
+ pixel type is double precision or complex. Note that the minimum and
+ maximum header values are still stored as real however.
+ (Davis 5/16/91)
+
+imarith/t_imarith.x
+ There was a missing statement to set the error flag if the image
+ dimensions did not match. (5/14/91, Valdes)
+
+doc/imarith.hlp
+ Fixed some formatting problems in the imarith help page. (5/2/91 Davis)
+
+imarith$imcombine.x
+ Changed the order in which images are unmapped to have the output images
+ closed last. This is to allow file descriptors for the temporary image
+ used when updating STF headers. (4/22/91, Valdes)
+
+pkg$images/geometry/t_blkavg.x
+pkg$images/geometry/blkavg.gx
+pkg$images/geometry/blkavg.x
+ The blkavg task was partially modified to support complex image data.
+ The full modifications cannot be made because of an error in abavx.x
+ and the missing routine absux.x.
+ (4/18/91 Davis)
+
+pkg$images/geometry/geofit.x
+ The x and y fits cross-terms switch was not being set correctly to "yes"
+ in the case where xxorder=2 and xyorder=2 or in the case where yxorder=2
+ and yyorder=2.
+ (4/9/91 Davis)
+
+pkg$images/geometry/geogmap.x
+ Modified the line which prints the geometric parameters to use the
+ variable name xshift and yshift instead of delx and dely.
+ (4/9/91 Davis)
+
+pkg$images/imfit/imsurfit.x
+ Fixed a bug in the pixel rejection code which occurred when upper was >
+ 0.0 and lower = 0.0 or lower > 0 and upper = 0.0. The problem was that
+ the code was simply setting the rejection limits to the computed sigma
+ times the upper and lower parameters without checking for the 0.0
+ condition first. In the first case this results in all points with
+ negative residuals being rejected and in the latter all points with
+ positive residuals are rejected.
+ (2/25/91 Davis)
+
+pkg$images/doc/hedit.hlp
+pkg$images/doc/hselect.hlp
+pkg$images/doc/imheader.hlp
+pkg$images/doc/imgets.hlp
+ Added a reference to imgets in the SEE ALSO sections of the hedit and
+ hselect tasks.
+ Added a reference to hselect and hedit in the SEE ALSO sections of the
+ imheader and imgets tasks.
+ (2/22/91 Davis)
+
+pkg$images/gradient.hlp
+pkg$images/laplace.hlp
+pkg$images/gauss.hlp
+pkg$images/convolve.hlp
+pkg$images/gradient.par
+pkg$images/laplace.par
+pkg$images/gauss.par
+pkg$images/convolve.par
+pkg$images/t_gradient.x
+pkg$images/t_laplace.x
+pkg$images/t_gauss.x
+pkg$images/t_convolve.x
+pkg$images/convolve.x
+pkg$images/xyconvolve.x
+pkg$images/radcnv.x
+ The convolution operators were modified to run more efficiently in
+ certain cases. The LAPLACE task was modified to make use of the
+ radial symmetry of the convolution kernel in the y direction as well
+ as the x direction resulting in a modest speedup in execution time.
+ A new parameter bilinear was added to the GAUSS and CONVOLVE tasks.
+ By default and if appropriate mathematically, GAUSS now makes use of
+ the bilinearity or separability of the Gaussian function,
+ to separate the 2D convolution in x and y into two equivalent
+ 1D convolutions in x and y, resulting in a considerable speedup
+ in execution time. Similarly the user can know program CONVOLVE to
+ compute a bilinear convolution instead of a full 2D 1 if appropriate.
+ (1/29/91 Davis)
+
+pkg$images/filters/t_convolve.x
+ CONVOLVE was not decoding the legal 1D kernel "1.0 2.0 1.0" correctly
+ although the alternate form "1.0 2.0 1.0;" worked. Leading
+ blanks in string kernels as in for example " 1.0 2.0 1.0" also generated
+ and error. Fixed these bugs and added some additional error checking code.
+ (11/28/90 Davis)
+
+pkg$images/doc/gauss.hlp
+ Added a detailed mathematical description of the gaussian kernel used
+ by the GAUSS task to the help page.
+
+pkg$images/images.hd
+pkg$images/rotate.cl
+pkg$images/imlintran.cl
+pkg$images/register.cl
+pkg$images/register.par
+ Added src="script file name" entries to the IMAGES help database
+ for the tasks ROTATE, IMLINTRAN, and REGISTER. Changed the CL
+ script for REGISTER to a procedure script to remove the ugly
+ local variable declarations. Added a few comments to the scripts.
+ (12/11/90, Davis)
+
+pkg$images/iminfo/imhistogram.x
+ Added a new parameter binwidth to imhistogram. If binwidth is defined
+ it determines the histogram resolution in intensity units, otherwise
+ nbins determines the resolution as before. (10/26/90, Davis)
+
+pkg$images/doc/sections.hlp
+ Clarified what is meant by an image template and that the task itself
+ does not check whether the specified names are actually images.
+ The examples were improved. (10/3/90, Valdes)
+
+pkg$images/doc/fit1d.hlp
+ Changed lines to columns in example 2. (10/3/90, Valdes)
+
+pkg$images/imarith/imcscales.x
+ When an error occured while parsing the mode section the untrapped error
+ caused further problems downstream. Because it would require adding
+ lots of errchks to cause the program to gracefully abort I instead made
+ it a warning. (10/2/90, Valdes)
+
+pkg$images/imutil/hedit.x
+ Hedit was computing but not using min_lenarea. If the user specified
+ a min_lenuserarea greater than the default of 28800 then the default
+ was being used instead of the larger number.
+
+pkg$imarith/imasub.gx
+ The case of subtracting an image from the constant zero had a bug
+ which is now fixed. (8/14/90, Valdes)
+
+pkg$images/t_imtrans.x
+ Modified the imtranspose task so it will work on type ushort images.
+ (6/6/90 Davis)
+
+pkg$images
+ Added world coordinate system support to the following tasks: imshift,
+ shiftlines, magnify, imtranspose, blkrep, blkavg, rotate, imlintran,
+ register and geotran. The only limitation is that register and geotran
+ will only support simple linear transformations.
+ (2/24/90 Davis)
+
+pkg$images/geometry/geotimtran.x
+ Fixed a problem in the boundary extension "reflect" option code for small
+ images which was causing odd values to be inserted at the edges of the
+ image.
+ (2/14/90 Davis)
+
+pkg$images/iminfo/imhistogram.x
+ A new parameter "hist_type" was added to the imhistogram task giving
+ the user the option of plotting the integral, first derivative and
+ second derivative of the histogram as well as the normal histogram.
+ Code was contributed by Rob Seaman.
+ (2/2/90 Davis)
+
+pkg$images/geometry/geogmap.x
+ The path name of the help file was being erroneously renamed with
+ the result that when users ran the double precision version of the
+ code they could not find the help file.
+ (26/1/90 Davis)
+
+pkg$images/filters/t_boxcar.x,t_convolve.x
+ Added some checks for 1-D images.
+ (1/20/90 Davis)
+
+pkg$images/iminfo/t_imstat.x,imstat.h
+ Made several minor bug fixes and alterations in the imstatistics task
+ in response to user complaints and suggestions.
+
+ 1. Changed the verbose parameter to the format parameter. If format is
+ "yes" (the default) then the selected fields are printed in fixed format
+ with column labels. Other wise the fields are printed in free format
+ separated by 2 blanks. This fixes the problem of fields running together.
+
+ 2. Fixed a bug in the code which estimates the median from the image
+ histogram by linearly interpolating around the midpt of the integrated
+ histogram. The bug occurred when more than half the pixels were in the
+ first bin.
+
+ 3. Added a check to ensure that the number of fields did not overflow
+ the fields array.
+
+ 4. Removed the extraneous blank line printed after the title.
+
+ 5. The pound sign is now printed at the beginning of the column header
+ string regardless of which field is printed first. In the previous
+ versions it was only being printed if the image name field was
+ printed first.
+
+ 6. Changed the name of the median field to midpt in response to user
+ confusions about how the median is computed.
+
+ (1/20/90, Davis)
+
+pkg$images/imutil/t_imslice.hlp
+ The imslice was not correctly computing the number of lines in the
+ output image in the case where the slice dimension was 1.
+ (12/4/89, Davis)
+
+pkg$images/doc/imcombine.hlp
+ Clarified and documented definitions of the scale, offset, and weights.
+ (11/30/89, Valdes)
+
+pkg$images/geometry/geotran.x
+ High order surfaces of a certain functional form could occasionally
+ produce out of bounds pixel errors. The bug was caused by not properly
+ computing the distortion of the image boundary for higher order
+ surfaces.
+ (11/21/89, Davis)
+
+pkg$images/geometry/imshift.x
+ The circulating buffer space was not being freed after each execution
+ of IMSHIFT. This did not cause an error in execution but for a long
+ list of frames could result in alot of memory being tied up.
+ (10/25/89, Davis)
+
+pkg$images/imarith/t_imarith.x
+ IMARITH is not prepared to deal with images sections in the output.
+ It used to look for '[' to decide if the output specification included
+ and image section. This has been changed to call the IMIO procedure
+ imgsection and check if a non-null section string is returned.
+ Thus it is up to IMIO to decide what part of the image name is
+ an image section. (9/5/89, Valdes)
+
+pkg$images/imarith/imcmode.gx
+ Fixed bug causing infinite loop when computing mode of constant value
+ section. (8/14/89, Valdes)
+
+====
+V2.8
+====
+
+pkg$images/iminfo/t_imstat.x
+ Davis, Jun 15, 1989
+ Added a couple of switches to that skew and kurtosis are not computed
+ if they are not to be printed.
+
+pkg$images/iminfo/t_imstat.x
+ Davis, Jun 14, 1989
+ A simple mod was made to the skew and kurtosis computation to avoid
+ divide by zero errors in case of underflow.
+
+pkg$images/imutil/chpixtype.par
+ Davis, Jun 13, 1989
+ The parameter file has been modified to accept an output pixel
+ type of ushort.
+
+pkg$images/imarith/imcombine.gx
+ Valdes, Jun 2, 1989
+ A new scheme to detect file errors is now used.
+
+pkg$images/imfit/t_imsurfit.x
+ Davis, Jun 1, 1989
+ 1. If the user set regions = "sections" but the sections file
+ did not exist the task would go into an infinite loop. The problem
+ was a missing error check on the open statement.
+
+pkg$images/iminfo/imhistogram.x,imhistogram.par
+ Davis, May 31, 1989
+ A new version of imhistogram has been installed. These mods have
+ been made over a period of a month by Doug Tody and Rob Seaman.
+ The mods include
+ 1. An option to turn off log scaling of the y axis of the histogram plot.
+ 2. A new autoscale parameter which avoids aliasing problems for integer
+ data.
+ 3. A new parameter top_close which resolves the ambiguity in the top
+ bin of the histogram.
+
+pkg$images/imarith/imcombine.gx
+ Valdes, May 9, 1989
+ Because a file descriptor was not reserved for string buffer operations
+ and a call to stropen in cnvdate was not error checked the task would
+ hang when more than 115 images were combined. Better error checking
+ was added and now an error message is printed when the maximum number
+ of images that can be combined is exceeded.
+
+pkg$images/imarith/t_imarith.x
+ Valdes, May 6, 1989
+ Operations in which the output image has an image section are now
+ skipped with a warning message.
+
+pkg$images/imarith/sigma.gx
+pkg$images/imarith/imcmode.gx
+ Valdes, May 6, 1989
+ 1. The weighted sigma was being computed incorrectly.
+ 2. The argument declarations were wrong for integer input images.
+ Namely the mean vector is always real.
+ 3. Minor change to imcmode.gx to return correct datatype.
+
+pkg$images/imstack,imslice
+ Davis, April 1, 1989
+ The proto images tasks imstack and imslice have been moved from the
+ proto package to the images package. Imstack is unchanged except that
+ it now supports the image data types USHORT and COMPLEX. Imslice has
+ been modified to allow slicing along any dimension of the image instead
+ of just the highest dimension.
+
+pkg$images/imstatistics.
+ Davis, Mar 31, 1989
+ 1. A totally new version of the imstatistics task has been written
+ and replaces the old version. The new task allows the user to select
+ which statistical parameters to compute and print. These include
+ the mean, median, mode, standard deviation, skew, kurtosis and the
+ minimum and maximum pixel values.
+
+pkg$images/imhistogram.par
+pkg$images/iminfo/imhistogram.x
+pkg$images/doc/imhistogram.hlp
+ Davis, Mar 31, 1989
+ 1. The imhistogram task has been modified to plot "box" style histograms
+ as well as "line" type histograms. Type "line" remains the default.
+
+pkg$images/geometry/geotran.par,register.par,geomap.par
+pkg$images/doc/geomap.hlp,register.hlp,geotran.hlp
+ Davis, Mar 6, 1989
+ 1. Improved the parameter prompting in GEOMAP, REGISTER and GEOTRAN
+ and improved the help pages.
+ 2. Changed GEOMAP database quantities "xscale" and "yscale" to "xmag"
+ and "ymag" for consistency . Geotran was changed appropriately.
+
+pkg$images/imarith/imcmode.gx
+ For short data a short variable was wraping around when there were
+ a significant number of saturated pixels leading to an infinite loop.
+ The variables were made real regardless of the image datatype.
+ (3/1/89, Valdes)
+
+pkg$images/imutil/imcopy.x
+ Davis, Feb 28, 1989
+ 1. Added support for type USHORT to the imcopy task. This is a merged
+ ST modification.
+
+pkg$images/imarith/imcthreshold.gx
+pkg$images/imcombine.par
+pkg$images/doc/imcombine.hlp
+pkg$images/imarith/imcscales.x
+ Valdes, Feb 16, 1989
+ 1. Added provision for blank value when all pixels are rejected by the
+ threshold.
+ 2. Fixed a bug that was improperly scaling images in the threshold option.
+ 3. The offset printed in the log now has the opposite sign so that it
+ is the value "added" to bring images to a common level.
+
+pkg$images/imfit/imsurfit.x
+ Davis, Feb 23, 1989
+ Fixed a bug in the median fitting code which could cause the porgram
+ to go into an infinite loop if the region to be fitted was less than
+ the size of the whole image.
+
+pkg$images/geometry/t_magnify.x
+ Davis, Feb 16, 1989
+ Modified magnify to work on 1D images as well as 2D images. The
+ documentation has been updated.
+
+pkg$images/geometry/t_geotran.x
+ Davis, Feb 15, 1989
+ Modified the GEOTRAN and REGISTER tasks so that they can handle a list
+ of transform records one for each input image.
+
+pkg$images/imarith/imcmode.gx
+ Valdes, Feb 8, 1989
+ Added test for nx=1.
+
+pkg$images/imarith/t_imcombine.x
+ Valdes, Feb 3, 1989
+ The test for the datatype of the output sigma image was wrong.
+
+pkg$images/iminfo/listpixels.x,listpixels.par
+ Davis, Feb 6, 1989
+ The listpixels task has been modified to print out the pixels for a
+ list of images instead of a single image only. A title line for each
+ image listed can optionally be printed on the standard output if
+ the new parameter verbose is set to yes.
+
+pkg$images/geometry/t_imshift.x
+ Davis, Feb 2, 1989
+ Added a new parameter shifts_file to the imshift task. Shifts_file
+ is the name of a text file containing the the x and yshifts for
+ each input image to be shifted. The number of input shifts must
+ equal the number of input images.
+
+pkg$images/geometry/t_geomap.x
+ Davis, Jan 17, 1989
+ Added an error message for the case where the coordinates is empty
+ of there are no points in the specified data range. Previously the
+ task would proceed to the next coordinate file without any message.
+
+pkg$images/geometry/t_magnify.x
+ Davis, Jan 14, 1989
+ Added the parameter flux conserve to the magnify task to bring it into
+ line with all the other geometric transformation tasks.
+
+pgk$images/geometry/geotran.x,geotimtran.x
+ Davis, Jan 2, 1989
+ A bug was fixed in the flux conserve code. If the x and y reference
+ coordinates are not in pixel units and are not 1 then
+ the computed flux per pixel was too small by xscale * yscale.
+
+pkg$images/filters/acnvrr.x,convolve.x,boxcar.x,aboxcar.x
+ Davis, Dec 27, 1988
+ I changed the name of the acnvrr procedure to cnv_radcnvr to avoid
+ a name conflict with a vops library procedure. This only showed
+ up when shared libraries were implemented. I also changed the name
+ of the aboxcarr procedure to cnv_aboxr to avoid conflict with the
+ vops naming conventions.
+
+pkg$images/imarith/imcaverage.gx
+ Davis, Dec 22, 1988
+ Added an errchk statement for imc_scales and imgnl$t to stop the
+ program bombing with segmentation violations when mode <= 0.
+
+pkg$images/imarith/imcscales.x
+ Valdes, Dec 8, 1988
+ 1. IMCOMBINE now prints the scale as a multiplicative quantity.
+ 2. The combined exposure time was not being scaled by the scaling
+ factors resulting in a final exposure time inconsistent with the
+ data.
+
+pkg$images/iminfo/imhistogram.x
+ Davis, Nov 30, 1988
+ Changed the list+ mode so that bin value and count are printed out instead
+ of bin count and value. This makes the plot and list modes compatable.
+
+pkg$images/iminfo/t_imstat.x
+ Davis, Nov 17, 1988
+ Added the n=n+1 back into the inner loop of imstat.
+
+pkg$images/geotran.par,register.par
+ Davis, Nov 11 , 1988
+ Fixed to glaring errors in the parameter files for register and geotran.
+ Xscale and yscale were described as pixels per reference unit when
+ they should be reference units per pixel. The appropriate bug fix has been
+ made.
+
+pkg$images/geometry/t_geotran.x
+ Davis, November 7, 1988
+ The routine gsrestore was not being error checked. If either of the
+ input x or y coordinate surface was linear and the other was not,
+ the message came back GSRESTORE: Illegal x coordinate. This bug has
+ been fixed.
+
+pkg$images/imarith/imcombine.gx
+ Valdes, October 19, 1988
+ A vops clear routine was not called generically causing a crash with
+ double images.
+
+pkg$images/filters/t_fmedian.x,t_median.x,t_fmode.x,t_mode.x,t_gradient.x
+ t_gauss.x,t_boxcar.x,t_convolve.x,t_laplace.x
+ Davis, October 4, 1988
+ I fixed a bug in the error handling code for the filters tasks. If
+ and error occurred during task execution and the input image name was
+ the same as the output image name then the input image was trashed.
+
+pkg$images/imarith/imcscales.gx
+ Valdes, September 28, 1988
+ It is now an error for the mode to be nonpositive when scaling or weighting.
+
+pkg$images/imarith/imcmedian.gx
+ Valdes, August 16, 1988
+ The median option was selecting the n/2 value instead of (n+1)/2. Thus,
+ for an odd number of images the wrong value was being determined for the
+ median.
+
+pkg$images/geometry/t_imshift.x
+ Davis, August 11, 1988
+ 1. Imshift has been modified to uses the optimized code if nearest
+ neighbour interpolation is requested. A nint is done on the shifts
+ before calling the quick shift routine.
+ 2. If the requested pixel shift is too large imshift will now
+ clean up any pixelless header files before continuing execution.
+
+pkg$images/geometry/blkavg.gx
+ Davis, July 13, 1988
+ Blkavg has been fixed so that it will work on 1D images.
+
+pkg$images/geometry/t_imtrans.x,imtrans.x
+ Davis, July 12, 1988
+ Imtranspose has been modified to work on complex images.
+
+pkg$images/imutil/t_chpix.x
+ Davis, June 29, 1988
+ A new task chpixtype has been added to the images package. Chpixtype
+ changes the pixel types of a list of images to a specified output pixel
+ type. Seven data types are supported "short", "ushort", "int", "long"
+ "real" and "double".
+
+pkg$images/geometry/rotate.cl,imlintran.cl,t_geotran.x
+ Davis, June 10, 1988
+ The rotate and imlintran scripts have been rewritten to use procedure
+ scripts. This removes all the annoying temporary cl variables which
+ appear when the user does an lpar. In previous versions of these
+ two tasks the output was restricted to being the same size as the input
+ image. This is still the default case, but the user can now set the
+ ncols and nrows parameters to the desired output size. I ncols or nlines
+ < 0 then then the task compute the output image size required to contain
+ the whole input image.
+
+pkg$images/filters/t_convolve.x,t_laplace.x,t_gradient.x,t_gauss.x,convolve.x
+ Davis, June 1, 1988
+ The convolution operators laplace, gauss and convolve have been modified
+ to make use of radial symmetry in the convolution kernel. In gauss and
+ laplace the change is transparent to the user. For the convolve operator
+ the user must indicate that the kernel is radially symmetric by setting
+ the parameter radsym. For kernels of 7 by 7 or greater the speedup
+ in timings is on the order of 30% on the Vax 750 with the fpa.
+
+pkg$images/imarith/imcmode.gx
+ Valdes, Apr 11, 1988
+ 1. The use of a mode sections was handled incorrectly.
+
+pkg$images/imfit/fit1d.x
+ Valdes, Jan 4, 1988
+ 1. Added an error check for a failure in IMMAP. The missing error check
+ caused FIT1D to hang when a bad input image was specified.
+
+pkg$images/magnify.par
+pkg$images/imcombine.par
+pkg$images/imarith/imcmode.gx
+pkg$images/doc/imarith.hlp
+ Valdes, Dec 7, 1987
+ 1. Added option list to parameter prompts.
+ 2. Fixed minor typo in help page
+ 3. The mode calculation in IMCOMBINE would go into an infinite loop
+ if all the pixel values were the same. If all the pixels are the
+ same them it skips searching for the mode and returns the constant
+ number.
+
+pkg$images/geometry/geotimtran.x
+ Davis, Nov 25, 1987
+ 1. A bug in the boundary extension = wrap option was found in the
+ IMLINTRAN task. The problem occured in computing values for out of
+ bounds pixels in the range 0.0 < x < 1.0, ncols < x < ncols + 1.0,
+ 0.0 < y < 1.0 and nlines < y < nlines + 1. The computed coordinates
+ were falling outside the boundaries of the interpolation array.
+
+pkg$images/geometry/t_geomap.x,geograph.x
+ Davis, Nov 19, 1987
+ 1. The geomap task now writes the name of the output file into the database.
+ 2. Rotation angles of 360. degrees have been altered to 0 degrees.
+
+pkg$images/imfit/t_imsurfit.x,imsurfit.x
+pkg$images/lib/ranges.x
+ Davis, Nov 2, 1987
+ A bug in the regions fitting option of the IMSURFIT task has been found
+ and fixed. This bug would occur when the user set the regions parameter
+ to sections and then listed section which overlapped each other. The
+ modified ranges package was not handling the overlap correctly and
+ computing a number of points which was incorrect.
+
+pkg$images/imarith/* +
+ Valdes, Sep 30, 1987
+ The directory was reorganized to put generic code in the subdirectory
+ generic.
+
+ A new task called IMCOMBINE has been added. It provides for combining
+ images by a number of algorithms, statistically weighting the images
+ when averaging, scaling or offsetting the images by the exposure time
+ or image mode before combining, and rejecting deviant pixels. It is
+ almost fully generic including complex images and works on images of
+ any dimension.
+
+pkg$images/geometry/geotran.x
+ Davis, Sept 3, 1987
+ A bug in the flux conserving algorithm was found in the geotran code.
+ The symptom was that the flux of the output image occasionally was
+ negative. This would happen when two conditions were met, the transformation
+ was of higher order than a simple rotation, magnification, translation
+ and an axis flip was involved. The mathematical interpretation of this
+ bug is that the coordinate surface had turned upside down. The solution
+ for people running systems with this bug is to multiply there images
+ by -1.
+
+pkg$images/imfit/imsurfit.h,t_imsurfit.x
+ Davis, Aug 6, 1987
+ A new option was added to the parameter regions in the imsurfit task.
+ Imsurfit will now fit a surface to a single circular region defined
+ by an x and y center and a radius.
+
+pkg$images/geometry/geotimtran.x
+ Davis, Jun 15, 1987
+ Geotran and register were failing when the output image number of rows
+ and columns was different from the input number of rows and columns.
+ Geotran was mistakenly using the input images sizes to determine the
+ number of output lines that should be produced. The same problem occurred
+ when the values of the boundary pixels were being computed. The program
+ was using the output image dimensions to compute the boundary pixels
+ instead of the input image dimensions.
+
+pkg$images/geometry/geofit.x,geogmap.x
+ Davis, Jun 11, 1987
+ A bug in the error checking code in the geomap task was fixed. The
+ condition of too few points for a reasonable was not being trapped
+ correctly. The appropriate errchk statements were added.
+
+pkg$images/geomap.par
+ Davis, Jun 10, 1987
+ The default fitting function was changed to polynomial. This will satisfy
+ most users who wish to do shifts, rotations, and magnifications and
+ avoid the neccessity of correctly setting the xmin, xmax, ymin, and ymax
+ parameters. For the chebyshev and legendre polynomial functions these
+ parameters must be explicitly set. For reference coordinates in pixel
+ units the normal settings are 1, ncols, 1 and nlines respectively.
+
+pkg$images/iminfo/hselect.x,imheader.x,images$/imutil/hselect.x
+ Davis, Jun 8, 1987
+ Imheader has been modified to open an image with the default min_lenuserarea
+ Hselect and hedit will now open the image setting the user area to the
+ maximum of 28800 chars or the min_lenuser environment variable.
+
+pkg$images/iminfo/t_imstat.x
+ Davis, May 22, 1987
+ An error in the image minimum computation was corrected. This error
+ would show up most noiticeably if imstat was run on a 1 pixel image.
+ The min value would be left set to MAX_REAL.
+
+pkg$images/filters/mkpkg
+ Davis, May 22, 1987
+ I added mach.h to the dependency file list of t_fmedian.x and
+ recompiled. The segmentation violations I had been getting in the
+ program disappeared.
+
+pkg$images/t_shiftlines.x,shiftlines.x
+ Davis, April 15, 1987
+ 1. I changed the names of the procedures shiftlines and shiftlinesi
+ to sh_lines and sh_linesi. When the original names were contracted
+ to 6 letter fortran names they became shifti and shifts which just
+ so happens to collide with shifti and shifts in the subdirectory
+ osb. On VMS this was causing problems with the shareable libraries.
+ If images was linked with -z there was no problem.
+
+pkg$images/imarith/t_imsum.x
+ Valdes, March 24, 1987
+ 1. IMSUM was failing to unmap images opened to check image dimensions
+ in a quick first pass through the image list. This is probably
+ the source of the out of files problem with STF images. It may
+ be the source of the out of memory problem reported from AOS/IRAF.
+
+pkg$images/imfit/fit1d.x
+pkg$images/imfit/mkpkg
+ Valdes, March 17, 1987
+ 1. Added error checking for the illegal operation in which both input
+ and output image had an image section. This was causing the task
+ to crash. The task now behaves properly in this circumstance and
+ even allows the fitted output to be placed in an image section of
+ an existing output image (even different than the input image
+ section) provided the input and output images have the same sizes.
+
+pkg$images/t_convolve.x
+ Davis, March 3, 1987
+ 1. Fixed the kernel decoding routine in the convolve task so that
+ it now recognizes the row delimter character in string entry mode.
+
+pkg$images/geometry,filters
+ Davis, February 27, 1987
+ 1. Changed all the imseti (im, TY_BNDRYPIXVAL, value) calls to imsetr.
+
+pkg$images/t_minmax.x,minmax.x
+ Davis, February 24, 1987
+ 1. Minmax has been changed to compute the minimum and maximum pixel
+ as well as the minimum and maximum pixel values. The pixels are output
+ in section notation and stored in the minmax parameter file.
+
+pkg$images/t_magnify.x
+ Davis, February 19, 1987
+ 1. Magnify was aborting with the error MSIFIT: Too few datapoints
+ when trying to reduce an image using the higher order interpolants
+ poly3, poly5 and spline3. I increased the NEDGE defined constant
+ from 2 to three and modified the code to use the out of bounds
+ imio.
+
+pkg$images/geograph.x,geogmap.x
+ Davis, February 17, 1987
+ 1. Geomap now uses the gpagefile routine to page the .keys file.
+ The :show command deactivates the workstation before printing a
+ block of text and reactivates it when it is finished.
+
+pkg$images/geometry/geomap,geotran
+ Davis, January 26, 1987
+ 1. There have been substantial changes to the geomap, and geotrans
+ tasks and those tasks rotate, imlintran and register which depend
+ on them.
+ 2. Geomap has been changed to be able to compute a transformation
+ in both single and double precision.
+ 3. The geotran code has been speeded up considerably. A simple rotate
+ now takes 70 seconds instead of 155 seconds using bilinear interpolation.
+ 4. Two new cl parameters nxblock and nyblock have been added to the
+ rotate, imlintran, register and geotran tasks. If the output image
+ is smaller than these parameters then the entire output image
+ is computed at once. Otherwise the output image is computed in blocks
+ nxblock by nyblock in size.
+ 5. The 3 geotran parameters rotation, scangle and flip have been replaced
+ with two parameters xrotation and yrotation which serve the same purpose.
+
+pkg$images/geometry/t_shiftlines.x,shiftlines.x
+ Davis, January 19, 1987
+ 1. The shiftlines task has been completely rewritten. The following
+ are the major changes.
+ 2. Shiftlines now makes use of the imio boundary extension operations.
+ Therefore the four options: nearest pixel, reflect, wrap and constant
+ boundary extension are available.
+ 3. The interpolation code has been vectorised. The previous version
+ was using the function call asieval for every output pixel evaluated.
+ The asieval call were replaced with asivector calls.
+ 4. An extra CL parameter constant to support constant boundary
+ exension was added.
+ 5. The shiftlines help page was modified and the date changed to
+ January 1987.
+
+pkg$images/imfit/imsurfit.x
+ Davis, January 12, 1987
+ 1. I changed the amedr call to asokr calls. For my application it did
+ not matter whether the input array is left partially sorted and the asokr
+ routine is more efficient.
+
+pkg$images/lib/pixlist.x
+ Davis, December 12, 1986
+ 1. A bug in the pl_get_ranges routine caused the routine to fail when the
+ number of ranges got too large. The program could not detect the end of
+ the ranges and would go into an infinite loop.
+
+pkg$images/iminfo/t_imstat.x
+ Davis, December 3, 1986
+ 1. Imstat was failing on constant images because finite machine precision
+ could result in a negative sigma squared. Added a check for this condition.
+
+pkg$images/filters/fmode.x
+ Davis, October 27, 1986
+ 1. Added a check for 0 data range before calling amapr.
+
+pkg$images/imarith/imsum.gx
+ Valdes, October 20, 1986
+ 1. Found and fixed bug in this routine which caused pixel rejection
+ to fail some fraction of the time.
+
+pkg$images/geometry/blkrp.gx
+ Valdes, October 13, 1986
+ 1. There was a bug when the replication factor for axis 1 was 1.
+
+pkg$images/iminfo/imhistogram.x
+ Hammond, October 8, 1986
+ 1. Running imhistogram on a constant valued image would result in
+ a "floating divide by zero fault" in ahgm. This condition is
+ now trapped and a warning printed if there is no range in the data.
+
+pkg$images/tv/doc/cvl.hlp
+ Valdes, October 7, 1986
+ 1. Typo in V2.3 documentation fixed: "zcale" -> "zscale".
+
+pkg$images/fit1d.par
+ Valdes, October 7, 1986
+ 1. When querying for the output type the query was:
+
+Type of output (fit, difference, ratio) (fit|difference|ratio) ():
+
+ The enumerated values were removed since they are given in the
+ prompt string.
+
+pkg$images/imarith/t_imsum.x
+pkg$images/imarith/imsum.gx
+pkg$images/do/imsum.hlp
+ Valdes, October 7, 1986
+ 1. Medians or pixel rejection with more than 15 images is now
+ correct. There was an error in buffering.
+ 2. Averages of integer datatype images are now correct. The error
+ was caused by summing the pixel values divided by the number
+ of images instead of summing the pixel values and then dividing
+ by the number of images.
+ 3. Option keywords may now be abbreviated.
+ 4. The output pixel datatype now defaults to the calculation datatype
+ as is done in IMARITH. The help page was modified to indicate this.
+ 5. Dynamic memory is now used throughout to reduce the size of the
+ executable.
+ 6. The bugs 1-2 are present in V2.3 and not in V2.2.
+
+pkg$images/imarith/t_imarith.x
+pkg$images/imarith.par
+pkg$images/doc/imarith.hlp
+ Valdes, October 6, 1986
+ 1. The parameter "debug" was changed to "noact". "debug" is reserved
+ for debugging information.
+ 2. The output pixel type now defaults to the calculation datatype.
+ 3. The datatype of constant operands is determined with LEXNUM. This
+ fixes a bug in which a constant such as "1." was classified as an
+ integer.
+ 4. Trailing whitespace in the string for a constant operand is allowed.
+ This fixes a bug with using "@" files created with the task FIELDS
+ from a table of numbers. Trailing whitespace in image names is
+ not checked for since this should be taken care of by lower level
+ system services.
+ 5. The reported bug with the "max" operation not creating a pixel file
+ was the result of the previous round of changes. This has been
+ corrected. This problem does not exist in the released version.
+ 6. All strings are now dynamically allocated. Also IMTOPENP is used
+ to open a CL list directly.
+ 7. The help page was revised for points (1) and (2).
+
+pkg$images/fmode.par
+pkg$images/fmd_buf.x
+pkg$images/med_sort.x
+ Davis, September 29, 1986
+ 1. Changed the default value of the unmap parameter in fmode to yes. The
+ documentation was changed and the date modified.
+ 2. Added a test to make sure that the input image was not a constant
+ image in fmode and fmedian.
+ 3. Fixed the recently added swap macro in the sort routines which
+ was giving erroneous results for small boxes in tasks median and mode.
+
+pkg$images/imfit/fit1d.x
+ Valdes, September 24, 1986
+ 1. Changed subroutine name with a VOPS prefix to one with a FIT1D
+ prefix.
+
+pkg$images/imarith/t_imdivide.x
+pkg$images/doc/imdivide.hlp
+pkg$images/imdivide.par
+ Valdes, September 24, 1986
+ 1. Modified this ancient and obsolete task to remove redundant
+ subroutines now available in the VOPS library.
+ 2. The option to select action on zero divide was removed since
+ there was only one option. Parameter file changed.
+ 3. Help page revised.
+
+pkg$images/geometry/t_blkrep.x +
+pkg$images/geometry/blkrp.gx +
+pkg$images/geometry/blkrep.x +
+pkg$images/doc/blkrep.hlp +
+pkg$images/doc/mkpkg
+pkg$images/images.cl
+pkg$images/images.men
+pkg$images/images.hd
+pkg$images/x_images.x
+ Valdes, September 24, 1986
+ 1. A new task called BLKREP for block replicating images has been added.
+ This task is a complement to BLKAVG and performs a function not
+ available in any other way.
+ 2. Help for BLKREP has been added.
+
+pkg$images/imarith/t_imarith.x
+pkg$images/imarith/imadiv.gx
+pkg$images/doc/imarith.hlp
+pkg$images/imarith.par
+ Valdes, September 24, 1986
+ 1. IMARITH has been modified to provide replacement of divisions
+ by zero with a constant parameter value.
+ 2. The documentation has been revised to include this change and to
+ clarify and emphasize areas of possible confusion.
+
+pkg$images/doc/magnify.hlp
+pkg$images/doc/blkavg.hlp
+ Valdes, September 18, 1986
+ 1. The MAGNIFY help document was expanded to clarify that images with axis
+ lengths of 1 cannot be magnified. Also a discussion of the output
+ size of a magnified image. This has been misunderstood often.
+ 2. Minor typo fix for BLKAVG.
+
+images$geometry/blkav.gx: Davis, September 7, 1986
+ 1. The routine blkav$t was declared a function but called everywhere as
+ a procedure. Removed the function declaration.
+
+images$filters/med_sort.x: Davis, August 14, 1986
+ 1. A bug in the sorting routine for MEDIAN and MODE in which the doop
+ loop increment was being set to zero has been fixed. This bug was
+ causing MEDIAN and MODE to fail on class 6 for certain sized windows.
+
+images$imfit/fit1d.x: Davis, July 24, 1986
+ 1. A bug in the type=ratio option of fit1d was fixed. The iferr call
+ on the vector operator adivr was not trapping a divide by zero
+ condition. Changed adivr to adivzr.
+
+images$iminfo/listpixels.x: Davis, July 21, 1986
+ 1. I changed a pargl to pargi for writing out the column number of the
+ pixels.
+
+images$iminfo/t_imstat.x: Davis, July 21, 1986
+ 1. I changed a pargr to a pargd for the double precision quantitiies
+ sum(MIN) and sum(MAX).
+
+images$imfit/t_lineclean.x: Davis, July 14, 1986
+ 1. Bug in the calling sequence for ic_clean fixed. The ic pointer
+ was not being passed to ic_clean causing access violation and/or
+ segmentation violation errors.
+
+images$imfit/fit1d.x, lineclean.x: Valdes, July 3, 1986
+ 1. FIT1D and LINECLEAN modified to use new ICFIT package.
+
+From Valdes June 19, 1986
+
+1. The help page for IMSUM was modified to explicitly state what the
+median of an even number of images does.
+
+-----------------------------------------------------------------------------
+
+From Davis June 13, 1986
+
+1. A bug in CONVOLVE in which insufficient space was being allocated for
+long (> 161 elements) 1D kernels has been fixed. CONVOLVE was not
+allocating sufficent extra space.
+
+-----------------------------------------------------------------------------
+
+From Davis June 12, 1986
+
+1. I have changed the default value of parameter unmap in task FMEDIAN to
+yes to preserve the original data range.
+
+2. I have changed the value of parameter row_delimiter from \n to ;.
+
+-----------------------------------------------------------------------------
+
+From Davis May 12, 1986
+
+1. Changed the angle convention in GAUSS so that theta is the angle of the
+major axis with respect to the x axis measured counter-clockwise as specified
+in the help page instead of the negative of that angle.
+
+-----------------------------------------------------------------------------
+
+From Davis Apr 28, 1986
+
+1. Moved geomap.key to lib$scr and made redefined HELPFILE in geogmap.x
+appropriately.
+
+------------------------------------------------------------------------------
+
+images$imarith/imsum.gx: Valdes Apr 25, 1986
+ 1. Fixed bug in generic code which called the real VOPS operator
+ regardless of the datatype. This caused IMSUM to fail on short
+ images.
+
+From Davis Apr 17, 1986
+
+1. Changed constructs of the form boolean == false in the file imdelete.x
+to ! boolean.
+
+------------------------------------------------------------------------------
+
+images$imarith: Valdes, April 8, 1986
+ 1. IMARITH has been modified to also operate on a list of specified
+ header parameters. This is primarily used when adding images to
+ also added the exposure times. A new parameter was added and the
+ help page modified.
+ 2. IMSUM has been modified to also operate on a list of specified
+ header parameters. This is primarily used when summing images to
+ also sum the exposure times. A new parameter was added and the
+ help page modified.
+
+------------------------------------------------------------------------------
+
+From Valdes Mar 24, 1986:
+
+1. When modifying IMARITH to handle mixed dimensions the output image header
+was made a copy of the image with the higher dimension. However, the default
+when the images were of the same dimension changed to be a copy of the second
+operand. This has been changed back to being a copy of the first operand
+image.
+
+------------------------------------------------------------------------------
+
+From Davis Mar 21, 1986:
+
+1. A NULL pointer bug in the subroutine plfree inside IMSURFIT was causing
+segmentation violation errors. A null pointer test was added to plfree.
+
+------------------------------------------------------------------------------
+
+From Davis Mar 20, 1986:
+
+1. A bug involving in place operations in several image tasks has been fixed.
+
+------------------------------------------------------------------------------
+
+From Davis Mar 19, 1986:
+
+1. IMSURFIT no longer permits the input image to be replaced by the output
+image.
+
+2. The tasks IMSHIFT, IMTRANSPOSE, SHIFTLINES, and GEOTRAN have been modified
+to use the images tools xt_mkimtemp and xt_delimtemp for in place
+calculations.
+
+-------------------------------------------------------------------------------
+
+From Valdes Mar 13, 1986:
+
+1. Bug dealing with type coercion in short datatype images in IMARITH and IMSUM
+which occurs on the SUN has been fixed.
+------
+From Valdes Mar 10, 1986:
+
+1. IMSUM has been modified to work on any number of images.
+
+2. Modified the help page
+------
+From Valdes Feb 25, 1986:
+
+There have been two changes to IMARITH:
+
+1. A bug preventing use of image sections has been removed.
+
+2. An improvement allowing use of images of different dimension.
+The algorithm is as follow:
+
+ a. Check if both operands are images. If not the output
+ image is a copy of the operand image.
+
+ b. Check that the axes lengths are the same for the dimensions
+ in common. For example a 3D and 2D image must have the same
+ number of columns and lines.
+
+ c. Set the output image to be a copy of the image with the
+ higher dimension.
+
+ d. Repeat the operation over the lower dimensions for each of
+ the higher dimensions.
+
+For example, consider subtracting a 2D image from a 3D image. The output
+image will be 3D and the 2D image is subtracted from each band of the
+3D image. This will work for any combination of dimensions. Another
+example is dividing a 3D image by a 1D image. Then each line of each
+plane and each band will be divided by the 1D image. Likely applications
+will be subtracting biases and darks and dividing by response calibrations
+in stacked observations.
+
+3. Modified the help page
+===========
+Release 2.2
+===========
+From Davis Mar 6, 1986:
+
+1. A serious bug had crept into GAUSS after I made some changes. For 2D
+images the sense of the sigma was reversed, i.e sigma = 2.0 was actually
+sigma = 0.5. This bug has now been fixed.
+
+---------------------------------------------------------------------------
+
+From Davis Jan 13, 1986:
+
+1. Listpixels will now print out complex pixel values correctly.
+
+---------------------------------------------------------------------------
+
+From Davis Dec 12, 1985:
+
+1. The directional gradient operator has been added to the images package.
+
+---------------------------------------------------------------------------
+
+From Valdes Dec 11, 1985:
+
+1. IMARITH has been modified to first check if an operand is an existing
+file. This allows purely numeric image names to be used.
+
+---------------------------------------------------------------------------
+
+From Davis Dec 11, 1985:
+
+1. A Laplacian (second derivatives) operator has been added to the images
+package.
+
+---------------------------------------------------------------------------
+
+From Davis Dec 10, 1985:
+
+1. The new convolution tasks boxcar, gauss and convolve have been added
+to the images package. Convolve convolves an image with an arbitrary
+user supplied rectangular kernel. Gauss convolves an image with a 2D
+Gaussian of arbitrary size. Boxcar will smooth an image using a smoothing
+window of arbitrary size.
+
+2. The images package source code has been reorganized into the following
+subdirectories: 1) filters 2) geometry 3) imfit 4) imarith 4) iminfo and
+5) imutil 6) lib. Lib contains routines which may be of use to several IRAF
+tasks such as ranges. The imutil subdirectory contains tasks which modify
+images in some way such as hedit. The iminfo subdirectory contains code
+for displaying header and pixel values and other image characteristics
+such as the histogram. Image arithmetic and fitting routines are found
+in imarith and imfit respectively. Filters contains the convolution and
+median filtering routines and geometry contains the geometric distortion
+corrections routines.
+
+3. The documentation of the main images package has been brought into
+conformity with the new IRAF standards.
+
+4. Documentation for imdelete, imheader, imhistogram, listpixels and
+sections has been added to the help database.
+
+5. The parameter structure for imhistogram has been simplified. The
+redundant parameters sections and setranges have been removed.
+
+---------------------------------------------------------------------------
+
+
+From Valdes Nov 4, 1985:
+
+1. IMCOPY modified so that the output image may be a directory. Previously
+logical directories were not correctly identified.
+------
+
+From Davis Oct 21, 1985:
+
+1. A bug in the pixel rejection cycle of IMSURFIT was corrected. The routine
+make_ranges in ranges.x was not successfully converting a sorted list of
+rejected pixels into a list of ranges in all cases.
+
+2. Automatic zero divide error checking has been added to IMSURFIT.
+------
+From Valdes Oct 17, 1985:
+
+1. Fit1d now allows averaging of image lines or columns when interactively
+setting the fitting parameters. The syntax is "Fit line = 10 30"; i.e.
+blank separated line or column numbers. A single number selects just one
+line or column. Be aware however, that the actual fitting of the image
+is still done on each column or line individually.
+
+2. The zero line in the interactive curve fitting graphs has been removed.
+This zero line interfered with fitting data near zero.
+------
+From Rooke Oct 10, 1985:
+
+1. Blkaverage was changed to "blkavg" and modified to support any allowed
+number of dimensions. It was also made faster in most cases, depending on
+the blocking factors in each dimension.
+------
+From Valdes Oct 4, 1985:
+
+1. Fit1d and lineclean modified to allow separate low and high rejection
+limits and rejection iterations.
+------
+From Davis Oct 3, 1985:
+
+1. Minmax was not calculating the minimum correctly for integer images.
+because the initial values were not being set correctly.
+------
+From Valdes Oct 1, 1985:
+
+1. Imheader was modified to print the image history. Though the history
+mechanism is little used at the moment it should become an important part
+of any image.
+
+2. Task revisions renamed to revs.
+------
+From Davis Sept 30, 1985:
+
+1. Two new tasks median and fmedian have been added to the images package.
+Fmedian is a fast median filtering algorithm for integer data which uses
+the histogram of the image to calculate the median at each window. Median
+is a slower but more general algorithm which performs the same task.
+------
+From Valdes August 26, 1985:
+
+1. Blkaverage has been modified to include an new parameter called option.
+The current options are to average the blocks or sum the blocks.
+------
+From Valdes August 7, 1985
+
+1. Fit1d and lineclean wer recompiled with the modified icfit package.
+The new package contains better labeling and graph documentation.
+
+2. The two tasks now have parameters for setting the graphics device
+and reading cursor input from a file.
+______
+From: /u2/davis/ Tue 08:27:09 06-Aug-85
+Package: images
+Title: imshift bug
+
+Imshift was shifting incorrectly when an integral pixel shift in x and
+a fractional pixel shift in y was requested. The actual x shift was
+xshift + 1. The bug has been fixed and imshift will now work correctly for
+any combination of fractional and integral pixel shifts
+------
+From: /u2/davis/ Fri 18:14:12 02-Aug-85
+Package: images
+Title: new images task
+
+A new task GEOMAP has been added to the images package. GEOMAP calculates
+the spatial transformation required to map one image onto another.
+------
+From: /u2/davis/ Thu 16:47:49 01-Aug-85
+Package: images
+Title: new images tasks
+
+The tasks ROTATE, IMLINTRAN and GEODISTRAN have been added to the images
+package. ROTATE rotates and shifts an image. IMLINTRAN will rotate, rescale
+and shift an an image. GEODISTRAN corrects an image for geometric distortion.
+------
+From Valdes July 26, 1985:
+
+1. The task revisions has been added to page revisions to the images
+package. The intent is that each package will have a revisions task.
+Note that this means there may be multiple tasks named revisions loaded
+at one time. Typing revisions alone will give the revisions for the
+current package. To get the system revisions type system.revisions.
+
+2. A new task called fit1d replaces linefit. It is essentially the same
+as linefit except for an extra parameter "axis" which selects the axis along
+which the functions are to be fit. Axis 1 is lines and axis 2 is columns.
+The advantages of this change are:
+
+ a. Column fitting can now be done without transposing the image.
+ This allows linefit to be used with image sections along
+ both axes.
+ b. For 1D images there is no prompt for the line number.
+.endhelp
diff --git a/pkg/images/immatch/doc/geomap.hlp b/pkg/images/immatch/doc/geomap.hlp
new file mode 100644
index 00000000..525e70b8
--- /dev/null
+++ b/pkg/images/immatch/doc/geomap.hlp
@@ -0,0 +1,435 @@
+.help geomap Jan01 images.immatch
+.ih
+NAME
+geomap -- compute one or more spatial transformation functions
+.ih
+USAGE
+geomap input database xmin xmax ymin ymax
+.ih
+PARAMETERS
+.ls input
+The list of text files containing the pixel coordinates of control points in
+the reference and input images. The control points are listed
+one per line with xref, yref, xin, and yin in columns 1 through 4 respectively.
+.le
+.ls database
+The name of the text file database where the computed transformations will
+be stored.
+.le
+.ls xmin, xmax, ymin, ymax
+The range of reference coordinates over which the computed coordinate
+transformation is valid. If the user is working in pixel units these limits
+should normally be set to the values of the column and row limits of the
+reference image, e.g xmin = 1.0, xmax = 512, ymin= 1.0, ymax = 512 for
+a 512 x 512 image. The minimum and maximum xref and yref values in \fIinput\fR
+are used if xmin, xmax, ymin, or ymax are undefined.
+.le
+.ls transforms = ""
+An optional list of transform record names. If transforms is undefined
+the database record(s) are assigned the names of the
+individual text files specified by \fIinput\fR.
+.le
+.ls results = ""
+Optional output files containing a summary of the results including a
+description of the transform geometry and a listing of the input coordinates,
+the fitted coordinates, and the fit residuals. The number of results files
+must be one or equal to the number of input files. If results is "STDOUT" the
+ results summary is printed on the standard output.
+.le
+.ls fitgeometry = "general"
+The fitting geometry to be used. The options are the following.
+.ls shift
+X and y shifts only are fit.
+.le
+.ls xyscale
+X and y shifts and x and y magnification factors are fit. Axis flips are
+allowed for.
+.le
+.ls rotate
+X and y shifts and a rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rscale
+X and y shifts, a magnification factor assumed to be the same in x and y, and a
+rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rxyscale
+X and y shifts, x and y magnifications factors, and a rotation angle are fit.
+Axis flips are allowed for.
+.le
+.ls general
+A polynomial of arbitrary order in x and y is fit. A linear term and a
+distortion term are computed separately. The linear term includes an x and y
+shift, an x and y scale factor, a rotation and a skew. Axis flips are also
+allowed for in the linear portion of the fit. The distortion term consists
+of a polynomial fit to the residuals of the linear term. By default the
+distortion term is set to zero.
+.le
+
+For all the fitting geometries except "general" no distortion term is fit,
+i.e. the x and y polynomial orders are assumed to be 2 and the cross term
+switches are assumed to be "none", regardless of the values of the
+\fIxxorder\fR, \fIxyorder\fR, \fIxxterms\fR, \fIyxorder\fR, \fIyyorder\fR and
+\fIyxterms\fR parameters set by the user.
+.le
+.ls function = "polynomial"
+The type of analytic surface to be fit. The options are the following.
+.ls legendre
+Legendre polynomials in x and y.
+.le
+.ls chebyshev
+Chebyshev polynomials in x and y.
+.le
+.ls polynomial
+Power series in x and y.
+.le
+.le
+.ls xxorder = 2, xyorder = 2, yxorder = 2, yyorder = 2
+The order of the polynomials in x and y for the x and y fits respectively.
+The default order and cross term settings define the linear term in x
+and y, where the 6 coefficients can be interpreted in terms of an x and y shift,
+an x and y scale change, and rotations of the x and y axes. The "shift",
+"xyscale", "rotation", "rscale", and "rxyscale", fitting geometries
+assume that the polynomial order parameters are 2 regardless of the values
+set by the user. If any of the order parameters are higher than 2 and
+\fIfitgeometry\fR is "general", then a distortion surface is fit to the
+residuals from the linear portion of the fit.
+.le
+.ls xxterms = "half", yxterms = "half"
+The options are:
+.ls none
+The individual polynomial terms contain powers of x or powers of y but not
+powers of both.
+.le
+.ls half
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is max (xxorder - 1, xyorder - 1) for the x fit and
+max (yxorder - 1, yyorder - 1) for the y fit.
+.le
+.ls full
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is max (xxorder - 1, xyorder - 1) for the x fit and
+max (yxorder - 1, yyorder - 1) for the y fit.
+.le
+
+The "shift", "xyscale", "rotation", "rscale", and "rxyscale" fitting
+geometries, assume that the cross term switches are set to "none"
+regardless of the values set by the user. If either of the cross terms
+parameters are set to "half" or "full" and \fIfitgeometry\fR is "general"
+then a distortion surface is fit to the residuals from the linear
+portion of the fit.
+.le
+.ls maxiter = 0
+The maximum number of rejection iterations. The default is no rejection.
+.le
+.ls reject = 3.0
+The rejection limit in units of sigma.
+.le
+.ls calctype = "real"
+The precision of the coordinate transformation calculations. The options are
+real and double.
+.le
+.ls verbose = yes
+Print messages about actions taken by the task ?
+.le
+.ls interactive = yes
+In interactive mode the user may interact with the fitting process, e.g.
+change the order of the fit, reject points, display the data, etc.
+.le
+.ls graphics = "stdgraph"
+The graphics device.
+.le
+.ls cursor = ""
+The graphics cursor.
+.le
+.ih
+DESCRIPTION
+
+GEOMAP computes the transformation required to map the reference coordinate
+system to the input coordinate system. The coordinates of points in common
+to the two systems are listed in the input text file(s) \fIinput\fR
+one per line in the following format: "xref yref xin yin".
+
+The computed transforms are stored in the text database file \fIdatabase\fR
+in records with names specified by the parameter \fItransforms\fR. If the
+transforms parameter is undefined the records are assigned the name of
+the input coordinate files.
+
+The computed transformation has the form shown below, where the reference
+coordinates must be defined in the coordinate system of the reference image
+system if the user intends to resample an image with gregister or geotran, or
+transform coordinates from the reference coordinate system to the input
+image coordinate system.
+
+.nf
+ xin = f (xref, yref)
+ yin = g (xref, yref)
+.fi
+
+If on the other hand the user wishes to transform coordinates from the
+input image coordinate system to the reference coordinate system then he or she
+must reverse the roles of the reference and input coordinates as defined above,
+and compute the inverse transformation.
+
+
+The functions f and g are either a power series polynomial or a Legendre or
+Chebyshev polynomial surface of order \fIxxorder\fR and \fIxyorder\fR in x
+and \fIyxorder\fR and \fIyyorder\fR in y.
+
+Several polynomial cross terms options are available. Options "none",
+"half", and "full" are illustrated below for a quadratic polynomial in
+x and y.
+
+.nf
+xxterms = "none", xyterms = "none"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a13' * yref ** 2
+
+xxterms = "half", xyterms = "half"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a22 * xref * yref + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a22' * xref * yref + a13' * yref ** 2
+
+xxterms = "full", xyterms = "full"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a31 * xref ** 2 +
+ a12 * yref + a22 * xref * yref + a32 * xref ** 2 * yref +
+ a13 * yref ** 2 + a23 * xref * yref ** 2 +
+ a33 * xref ** 2 * yref ** 2
+ yin = a11' + a21' * xref + a31' * xref ** 2 +
+ a12' * yref + a22' * xref * yref + a32' * xref ** 2 * yref +
+ a13' * yref ** 2 + a23' * xref * yref ** 2 +
+ a33' * xref ** 2 * yref ** 2
+.fi
+
+If the \fBfitgeometry\fR parameter is anything other than "general", the order
+parameters assume the value 2 and the cross terms switches assume the value
+"none", regardless of the values set by the user. The computation can be done in
+either real or double precision by setting \fIcalctype\fR. Automatic pixel
+rejection may be enabled by setting \fmaxiter\fR > 0 and \fIreject\fR to some
+number greater than 0.
+
+\fIXmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR define the region of
+validity of the fit in the reference coordinate system and must be set by
+the user. These parameters can be used to reject out of range data before the
+actual fitting is done.
+
+GEOMAP may be run interactively by setting \fIinteractive\fR = yes and
+inputting commands by the use of simple keystrokes.
+In interactive mode the user has the option of changing the
+fit parameters and displaying the data graphically until a satisfactory
+fit has been achieved. The available keystroke commands are listed
+below.
+
+.nf
+? Print options
+f Fit the data and graph with the current graph type (g, x, r, y, s)
+g Graph the data and the current fit
+x,r Graph the x fit residuals versus x and y respectively
+y,s Graph the y fit residuals versus x and y respectively
+d,u Delete or undelete the data point nearest the cursor
+o Overplot the next graph
+c Toggle the constant x, y plotting option
+t Plot a line of constant x, y through the nearest data point
+l Print xshift, yshift, xmag, ymag, xrotate, yrotate
+q Exit the interactive curve fitting
+.fi
+
+The parameters listed below can be changed interactively with simple colon
+commands. Typing the parameter name alone will list the current value.
+
+.nf
+:show List parameters
+:fitgeometry Fitting geometry (shift,xyscale,rotate,
+ rscale,rxyscale,general)
+:function [value] Fitting function (chebyshev,legendre,
+ polynomial)
+:xxorder :xyorder [value] X fitting function xorder, yorder
+:yxorder :yyorder [value] Y fitting function xorder, yorder
+:xxterms :yxterms [n/h/f] X, Y fit cross terms type
+:maxiter [value] Maximum number of rejection iterations
+:reject [value] Rejection threshold
+.fi
+
+The final fit is stored in a simple text file in a format suitable for use
+by the GREGISTER or GEOTRAN tasks.
+
+If \fIverbose\fR is "yes", various pieces of useful information are printed
+to the terminal as the task proceeds. If \fIresults\fR is set to a file name
+then the input coordinates, the fitted coordinates, and the residuals of
+the fit are written to that file.
+
+The transformation computed by the "general" fitting geometry is arbitrary
+and does not correspond to a physically meaningful model. However the computed
+coefficients for the linear term can be given a simple geometrical geometric
+interpretation for all the fitting geometries as shown below.
+
+.nf
+ fitting geometry = general (linear term)
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+
+ fitting geometry = shift
+ xin = a + xref
+ yin = d + yref
+
+ fitting geometry = xyscale
+ xin = a + b * xref
+ yin = d + f * yref
+
+ fitting geometry = rotate
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/-1
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rxyscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+.fi
+
+The coefficients can be interpreted as follows. Xref0, yref0, xin0, yin0
+are the origins in the reference and input frames respectively. Orientation
+and skew are the rotation of the x and y axes and their deviation from
+perpendicularity respectively. Xmag and ymag are the scaling factors in x and
+y and are assumed to be positive.
+
+.nf
+ general (linear term)
+ xrotation = rotation - skew / 2
+ yrotation = rotation + skew / 2
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ shift
+ xrotation = 0.0, yrotation = 0.0
+ xmag = ymag = 1.0
+ b = 1.0
+ c = 0.0
+ e = 0.0
+ f = 1.0
+ a = xin0 - xref0 = xshift
+ d = yin0 - yref0 = yshift
+
+ xyscale
+ xrotation 0.0 / 180.0 yrotation = 0.0
+ b = + /- xmag
+ c = 0.0
+ e = 0.0
+ f = ymag
+ a = xin0 - b * xref0 = xshift
+ d = yin0 - f * yref0 = yshift
+
+ rscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ mag = xmag = ymag
+ const = mag * mag
+ b = mag * cos (xrotation)
+ c = mag * sin (yrotation)
+ e = -mag * sin (xrotation)
+ f = mag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ rxyscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ const = xmag * ymag
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+.fi
+
+
+.ih
+EXAMPLES
+1. Compute the linear transformation between coordinate systems.
+ A record called "m51.coo" will be written in the database
+ file "database".
+
+
+.nf
+ cl> geomap m51.coo database 1. 512. 1. 512.
+.fi
+
+2. Compute the 3rd order transformation in x and y between two
+ coordinate systems. A record called "m51.coo" will be written in
+ the database file "database". This record supersedes the one
+ of the same name written in example 1.
+
+.nf
+ cl> geomap m51.coo database 1. 512. 1. 512. xxo=4 xyo=4 \
+ >>> yxo=4 yyo=4 xxt=full yxt=full inter-
+.fi
+
+3. Register a 500 by 500 image of m51 to an 800 by 800 image of the same
+field taken with a different instrument, and display the original
+800 by 800 image and the transformed image. Use the default fitting parameters.
+
+.nf
+ cl> geomap m51.coo database 1.0 800.0 1.0 800.0
+ cl> gregister m51.500 m51.500.out database m51.coo
+ cl> display m51.800 1 fi+
+ cl> display m51.500.out 2 fi+
+.fi
+
+4. Use the above transform to transform a list of object pixel coordinates
+in the m51.800 image to their pixel coordinates in the m51.500 system.
+
+.nf
+ cl> geoxytran m51.800.xy m51.500.xy database m51.coo
+.fi
+
+5. Transform object pixel coordinates in the m51.500 image to their
+pixel coordinates in the m51.800 image. Note that to do this the roles
+of the reference and input coordinates defined in example 3 must be
+reversed and the inverse transform must be computed.
+
+.nf
+ cl> fields m51.coo 3,4,1,2 > m51.coo.inv
+ cl> geomap m51.coo.inv database 1.0 512.0 1.0 512.0
+ cl> geoxytran m51.512.xy m51.800.xy database m51.coo.inv
+.fi
+
+6. Compute 3 different transforms, store them in the same database file,
+and use them to transform 3 different images. Use the original image names as
+the database record names.
+
+.nf
+ cl> geomap coo1,coo2,coo3 database 1. 512. 1. 512. \
+ >>> transforms=im1,im2,im3
+ cl> gregister im1,im2,im3 im1.out,im2.out,im3.out database \
+ >>> im1,im2,im3
+.fi
+
+.ih
+BUGS
+
+The user should be aware that for high order fits the "polynomial" basis
+functions become very unstable. Switching to the "legendre" or "chebyshev"
+polynomials and/or going to double precision will usually cure the problem.
+
+.ih
+SEE ALSO
+imshift, magnify, rotate, imlintran, gregister, geotran, geoxytran
+.endhelp
diff --git a/pkg/images/immatch/doc/geotran.hlp b/pkg/images/immatch/doc/geotran.hlp
new file mode 100644
index 00000000..e3ad15f7
--- /dev/null
+++ b/pkg/images/immatch/doc/geotran.hlp
@@ -0,0 +1,320 @@
+.help geotran Dec98 images.immatch
+.ih
+NAME
+geotran -- geometrically transform a list of images
+.ih
+USAGE
+geotran input output database transforms
+.ih
+PARAMETERS
+.ls input
+List of images to be transformed.
+.le
+.ls output
+List of output images. If the output image name is the same as the input
+image name the input image is overwritten. The output image may be a section
+of an existing image. The number of output images must equal the number
+of input images.
+.le
+.ls database
+The name of the text file containing the coordinate transformation (generally
+the database file produced by GEOMAP).
+If database is the null string then GEOTRAN will perform a linear
+transformation based the values of xin, yin, xout, yout, xshift, yshift,
+xmag, ymag, xrotation and yrotation. If all these parameters have their
+defaults values the transformation is a null transformation. If the geometric
+transformation is linear xin, yin, xout, yout, xshift, yshift, xmag, ymag,
+xrotation and yrotation can be used to override the values in the database
+file.
+.le
+.ls transforms
+The list of record name(s) in the file \fIdatabase\fR containing the
+desired transformations.
+This record name is usually the name of the text file input to geomap
+listing the reference and input coordinates of the control points.
+The number of records must be 1 or equal to the number of input images.
+The record names may be listed in a text file 1 record per line.
+The transforms parameter is only
+requested if database is not equal to the null string.
+.le
+.ls geometry = "geometric"
+The type of geometric transformation. The geometry parameter is
+only requested if database is not equal to the null string. The options are:
+.ls linear
+Perform only the linear part of the geometric transformation.
+.le
+.ls geometric
+Compute both the linear and distortion portions of the geometric correction.
+.le
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum x and y reference values of the output image.
+If a database file has been defined xmin, xmax, ymin and ymax
+efault to the minimum and maximum values set by
+GEOMAP and may be less than but may not exceed those values.
+.le
+.ls xscale = 1.0, yscale = 1.0
+The output picture x and y scales in units of
+x and y reference units per output pixel,
+e.g arcsec / pixel or Angstroms / pixel if the reference coordinates
+are arcsec or Angstroms. If the reference coordinates are in pixels
+then xscale and yscale should be 1.0 to preserve the scale of the reference
+image.
+If xscale and yscale are undefined (INDEF), xscale and yscale default to the
+range of the reference coordinates over the range in pixels.
+Xscale and yscale override the values of ncols and nlines.
+.le
+.ls ncols = INDEF, nlines = INDEF
+The number of columns and lines in the output image. Ncols and nlines default
+to the size of the input image. If xscale or yscale are defined ncols or nlines
+are overridden.
+.le
+.ls xsample = 1.0, ysample = 1.0
+The coordinate surface subsampling factor. The coordinate surfaces are
+evaluated at every xsample-th pixel in x and every ysample-th pixel in y.
+Transformed coordinates at intermediate pixel values are determined by
+bilinear interpolation in the coordinate surfaces. If the coordinate
+surface is of high order setting these numbers to some reasonably high
+value is strongly recommended.
+.le
+.ls interpolant = "linear"
+The interpolant used for rebinning the image.
+The choices are the following.
+.ls nearest
+Nearest neighbor.
+.le
+.ls linear
+Bilinear interpolation in x and y.
+.le
+.ls poly3
+Third order polynomial in x and y.
+.le
+.ls poly5
+Fifth order polynomial in x and y.
+.le
+.ls spline3
+Bicubic spline.
+.le
+.ls sinc
+2D sinc interpolation. Users can specify the sinc interpolant width by
+appending a width value to the interpolant string, e.g. sinc51 specifies
+a 51 by 51 pixel wide sinc interpolant. The sinc width will be rounded up to
+the nearest odd number. The default sinc width is 31 by 31.
+.le
+.ls lsinc
+Look-up table sinc interpolation. Users can specify the look-up table sinc
+interpolant width by appending a width value to the interpolant string, e.g.
+lsinc51 specifies a 51 by 51 pixel wide look-up table sinc interpolant. The user
+supplied sinc width will be rounded up to the nearest odd number. The default
+sinc width is 31 by 31 pixels. Users can specify the resolution of the lookup
+table sinc by appending the look-up table size in square brackets to the
+interpolant string, e.g. lsinc51[20] specifies a 20 by 20 element sinc
+look-up table interpolant with a pixel resolution of 0.05 pixels in x and y.
+The default look-up table size and resolution are 20 by 20 and 0.05 pixels
+in x and y respectively.
+.le
+.ls drizzle
+2D drizzle resampling. Users can specify the drizzle pixel fraction in x and y
+by appending a value between 0.0 and 1.0 in square brackets to the
+interpolant string, e.g. drizzle[0.5]. The default value is 1.0.
+The value 0.0 is increased internally to 0.001. Drizzle resampling
+with a pixel fraction of 1.0 in x and y is equivalent to fractional pixel
+rotated block summing (fluxconserve = yes) or averaging (flux_conserve = no) if
+xmag and ymag are > 1.0.
+.le
+.le
+.ls boundary = "nearest"
+The choices are:
+.ls nearest
+Use the value of the nearest boundary pixel.
+.le
+.ls constant
+Use a user supplied constant value.
+.le
+.ls reflect
+Generate a value by reflecting about the boundary of the image.
+.le
+.ls wrap
+Generate a value by wrapping around to the opposite side of the image.
+.le
+.le
+.ls constant = 0.0
+The value of the constant for boundary extension.
+.le
+.ls fluxconserve = yes
+Preserve the total image flux. The output pixel values are multiplied by
+the Jacobian of the coordinate transformation.
+.le
+.ls xin = INDEF, yin = INDEF
+The x and y coordinates in pixel units in the input image which will map to
+xout, yout in the output image. If the database file is undefined these
+numbers default to the center of the input image.
+.le
+.ls xout = INDEF, yout = INDEF
+The x and y reference coordinates in the output image which correspond
+to xin, yin in the input image. If the database file is undefined, xout and
+yout default to the center of the output image reference coordinates.
+.le
+.ls xshift = INDEF, yshift = INDEF
+The shift of the input origin in pixels. If the database file is undefined
+then xshift and yshift determine the shift of xin, yin.
+.le
+.ls xmag = INDEF, ymag = INDEF
+The scale factors of the coordinate transformation in units of input pixels
+per reference coordinate unit. If database is undefined xmag and ymag
+default to 1.0; otherwise xmag and ymag default to the values found
+by GEOMAP. If the database file is not null then xmag and ymag override
+the values found by GEOMAP.
+.le
+.ls xrotation = INDEF, yrotation = INDEF
+The rotation angles in degrees of the coordinate transformation.
+Positive angles are measured counter-clockwise with respect to the x axis.
+If database
+is undefined then xrotation and yrotation default to 0.0; otherwise
+xrotation and yrotation default to the values found by GEOMAP.
+If database is not NULL then xrotation and yrotation override the values
+found by GEOMAP.
+.le
+.ls nxblock = 512, nyblock = 512
+If the size of the output image is less than nxblock by nyblock then
+the entire image is transformed at once. Otherwise the output image
+is computed in blocks of nxblock by nxblock pixels.
+.le
+.ls verbose = yes
+Print messages about the progress of the task ?
+.le
+.ih
+DESCRIPTION
+
+GEOTRAN corrects an image for geometric distortion using the coordinate
+transformation determined by GEOMAP. The transformation is stored as the
+coefficients of a polynomial surface in record \fItransforms\fR,
+in the text file \fIdatabase\fR.
+The coordinate surface is sampled at every \fIxsample\fR and \fIysample\fR
+pixel in x and y.
+The transformed coordinates at intermediate pixel values are
+determined by bilinear interpolation in the coordinate surface. If
+\fIxsample\fR and \fIysample\fR = 1, the coordinate
+surface is evaluated at every pixel. Use of \fIxsample\fR and \fIysample\fR
+are strongly recommended for large images and high order coordinate
+surfaces in order to reduce the execution time.
+
+\fIXmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR define the range of
+reference coordinates represented in the output picture. These numbers
+default to the minimum and maximum x and y reference values used by GEOMAP,
+and may not exceed those values.
+The scale and size of the output picture is determined as follows.
+
+.nf
+ ncols = ncols (inimage)
+ if (xscale == INDEF)
+ xscale = (xmax - xmin ) / (ncols - 1)
+ else
+ ncols = (xmax - xmin) / xscale + 1
+
+ nlines = nlines (inimage)
+ if (yscale == INDEF)
+ yscale = (ymax - ymin ) / (nlines - 1)
+ else
+ nlines = (ymax - ymin) / yscale + 1
+.fi
+
+The output image gray levels are determined by interpolating in the input
+image at the positions of the transformed output pixels. If the
+\fIfluxconserve\fR switch is set the output pixel values are multiplied by
+the Jacobian of the transformation.
+GEOTRAN uses the routines in the 2-D interpolation package.
+
+The linear portion of the transformation may be altered after the fact
+by setting some or all of the parameters \fIxin\fR, \fIyin\fR, \fIxout\fR,
+\fIyout\fR, \fIxshift\fR, \fIyshift\fR, \fIxmag\fR, \fIymag\fR, \fIxrotation\fR,
+\fIyrotation\fR.
+Xin, yin, xshift, yshift, xout and yout can be used to redefine the shift.
+Xmag, and ymag can be used to reset the x and y scale of the transformation.
+Xrotation and yrotation can be used to reset the orientation of the
+transformation.
+
+The output image is computed in \fInxblock\fR by \fInyblock\fR pixel sections.
+If possible users should set these numbers to values larger than the dimensions
+of the output image to minimize the number of disk reads and writes required
+to compute the output image. If this is not feasible and the image rotation is
+small, users should set nxblock to be greater than the number of columns in
+the output image, and nyblock to be as large as machine memory will permit.
+
+If the CL environment variable \fInomwcs\fR is "no" then the world
+coordinate system of the input image will be modified in the output image
+to reflect the effects of the \fIlinear\fR portion of the geometric
+transformation operation.
+Support does not yet exist in the IRAF world coordinate system interface
+for the higher order distortion corrections that GEOTRAN is capable of
+performing.
+
+.ih
+TIMINGS
+It requires approximately 70 and 290 cpu seconds to correct a 512 by 512
+square image for geometric distortion using a low order coordinate surface
+and bilinear and biquintic interpolation respectively (Vax 11/750 fpa).
+
+.ih
+EXAMPLES
+
+1. Register two images by transforming the coordinate system of the input
+image to the coordinate system of the reference image. The size of the
+reference image is 512 by 512. The output image scale will be 1.0 and
+its size will be determined by the xmin, xmax, ymin, ymax parameters set
+in the task GEOMAP. The file "database" containing the record "m51.coo"
+was produced by GEOMAP.
+
+.nf
+ cl> geomap m51.coo database 1.0 512.0 1.0 512.0
+ cl> geotran m51 m51.tran database m51.coo
+.fi
+
+2. Repeat the above command but set the output image scale to 2.0 reference
+images pixels per output image pixel.
+
+.nf
+ cl> geomap m51.coo database 1.0 512.0 1.0 512.0
+ cl> geotran m51 m51.tran database m51.coo xscale=2.0 yscale=2.0
+.fi
+
+3. Repeat the previous command using an output scale of
+2 reference units per pixel and bicubic spline interpolation with no
+flux correction.
+
+.nf
+ cl> geomap m51.coo database 1.0 512.0 1.0 512.0
+ cl> geotran m51 m51.tran database m51.coo xscale=2. yscale=2. \
+ >>> inter=spline3 flux-
+.fi
+
+4. Register a list of 512 by 512 pixel square images using the set of
+transforms computed by GEOMAP. The input images, output images, and coordinate
+lists / transforms are listed in the files inlist, outlist and reclist
+respectively.
+
+.nf
+ cl> geomap @reclist database 1. 512. 1. 512.
+ cl> geotran @inlist @outlist database @reclist
+.fi
+
+5. Mosaic 3 512 square images into a larger 512 by 1536 square images after
+applying a shift to each input image.
+
+.nf
+ cl> geotran image1 outimage[1:512,1:512] "" ncols=512 nlines=1536 \
+ xshift=5.0 yshift=5.0
+ cl> geotran image2 outimage[1:512,513:1024] "" xshift=10.0 yshift=10.0
+ cl> geotran image3 outimage[1:512,1025:1536] "" xshift=15.0 yshift=15.0
+.fi
+
+.ih
+BUGS
+Support does not yet exist in the IRAF world coordinate system interface
+for the higher order distortion corrections that GEOTRAN is capable of
+performing.
+
+.ih
+SEE ALSO
+imshift, magnify, rotate, imlintran, geomap, geoxytran, gregister
+.endhelp
diff --git a/pkg/images/immatch/doc/geoxytran.hlp b/pkg/images/immatch/doc/geoxytran.hlp
new file mode 100644
index 00000000..69e8565c
--- /dev/null
+++ b/pkg/images/immatch/doc/geoxytran.hlp
@@ -0,0 +1,408 @@
+.help geoxytran Apr95 images.immatch
+.ih
+NAME
+geoxytran -- geometrically transform a list of coordinates
+.ih
+USAGE
+geoxytran input output database transforms
+.ih
+PARAMETERS
+.ls input
+The list of input coordinate files to be transformed.
+.le
+.ls output
+The list of output transformed coordinate files. The number of output files must
+be one or equal to the number of input files.
+.le
+.ls database
+The name of the text database file written by the geomap task which
+contains the desired spatial transformation.
+If database is undefined geoxytran computes
+a linear transformation using the current
+values of the xref, yref, xout, yout, xshift, yshift, xmag, ymag, xrotation,
+and yrotation parameters.
+.le
+.ls transforms
+The database record containing the desired spatial transformation.
+The number of records must be one or equal to the number of input coordinate
+files. Transforms is usually the name of the coordinate file that the
+geomap task used to compute the spatial transformation.
+If defined the values of xref, yref, xout, yout, xshift, yshift, xmag, ymag,
+xrotation, and yrotation will supersede the computed values in the
+database file.
+.le
+.ls geometry = "geometric" (linear|geometric)
+The type of geometric transformation. The geometry parameter is
+only requested if database is defined. The options are:
+.ls linear
+Perform only the linear part of the spatial transformation.
+.le
+.ls geometric
+Compute both the linear and distortion portions of the spatial transformation.
+.le
+.le
+.ls direction = "forward" (forward|backward)
+The transformation direction may be "forward" or "backward". The forward
+direction directly evaluates the database solution. The backward
+direction iteratively determines the coordinate which evaluates to the
+specified coordinate.
+.le
+.ls xref = INDEF, yref = INDEF
+The x and y coordinates of the reference origin.
+If the database file is undefined xref and
+yref default to [0.0,0.0]. Otherwise xref and yref
+default to the mean of minimum and maximum x and y values
+[(xmin + xmax) / 2.0, (ymin + ymax) / 2.0] computed by geomap.
+.le
+.ls xmag = INDEF, ymag = INDEF
+The x and y scale factors in input units
+per reference unit. If database is undefined xmag and ymag
+default to [1.0, 1.0]. Otherwise xmag and ymag default to the values computed
+by geomap.
+.le
+.ls xrotation = INDEF, yrotation = INDEF
+The x and y rotation angles in degrees measured counter-clockwise with
+respect to the x and y axes. If database
+is undefined then xrotation and yrotation are interpreted as the
+rotation of the coordinates with respect to the x and y axes and
+default to [0.0, 0.0]. For example xrotation and yrotation values of
+[30.0, 30.0] will rotate a point 30 counter-clockwise with respect
+to the x and y axes. Otherwise xrotation and yrotation default to the
+values computed by geomap. Geomap computes the x and y rotation angles
+of the x and y axes, not the rotation angle of the coordinates. An output
+coordinate system rotated 30 degrees counter-clockwise with respect
+to the reference coordinate system will produce xrotation and yrotation
+values of [330.0,330.0] or equivalently [-30.0,-30.0] in the database file
+not [30.0,30.0].
+.le
+.ls xout = INDEF, yout = INDEF
+The x and y coordinates of the output origin.
+If the database file is undefined xout and
+yout default to [0.0,0.0].
+If database is defined xout and yout
+default to the position that the reference origin [xref,yref]
+occupies in the transformed system.
+.le
+.ls xshift = INDEF, yshift = INDEF
+The x and y shift of the reference origin in output units.
+If the database file is undefined xshift and yshift default to [0.0,0.0].
+If the database file is defined xshift and yshift default to the
+values computed by geomap. If defined xshift and yshift take precedence over
+the x and y shifts determined from xref, yref, xout and yout.
+.le
+.ls xcolumn = 1, ycolumn = 2
+The columns in the input coordinate file containing the x and y coordinates.
+.le
+.ls calctype = "real"
+The precision of the coordinate transformation calculations. The options
+are "real" and "double". Note that this only applies to a "forward"
+transformation. The "backward" transformation is done iteratively and
+is always calculated in double precision to get the best convergence.
+.le
+.ls xformat = "", yformat = ""
+The default output format for the computed x and y coordinates. If
+xformat and yformat are undefined geoxytran outputs the coordinates
+using the maximum of the precision of the input coordinates
+and the value of the \fImin_sigdigits\fR parameter.
+.le
+.ls min_sigdigits = 7
+The minimum precision of the output x and y coordinates.
+.le
+
+.ih
+DESCRIPTION
+
+GEOXYTRAN applies a coordinate transformation to a list of reference
+coordinates in the text file \fIinput\fR and writes the transformed
+coordinates to the text file \fIoutput\fR. The input coordinates
+are read from, and the output coordinates written to, columns
+\fIxcolumn\fR and \fIycolumn\fR in the input and output
+files. The format of the output coordinates can be specified using the
+\fIxformat\fR and \fIyformat\fR parameters. If the output formats
+are unspecified the coordinates are written out with a precision
+which is the maximum of the precision of the input coordinates
+and the value of the \fImin_sigdigits\fR parameter. All remaining fields in
+the input file are copied to the output file without modification.
+Blank lines and comment lines are also passed to the output file
+unaltered.
+
+The coordinate transformation either be read from record \fItransforms\fR
+in the database file \fIdatabase\fR computed by GEOMAP, or specified
+by the user via the \fIxref\fR, \fIyref\fR, \fIxmag\fR, \fIymag\fR,
+\fIxrotation\fR, \fIyrotation\fR, \fIxout\fR, \fIyout\fR, \fIxshift\fR,
+and \fIyshift\fR parameters.
+
+The transformation computed by GEOMAP has the following form.
+
+.nf
+ xout = f (xref, yref)
+ yout = g (xref, yref)
+.fi
+
+The functions f and g are either a power series polynomial or a Legendre
+or Chebyshev polynomial surface whose order and region of validity were
+set by the user when GEOMAP was run. The computed transformation is
+arbitrary and does not correspond to any physically meaningful model.
+However the first order terms can be given the simple geometrical
+interpretation shown below.
+
+.nf
+ xout = a + b * xref + c * yref
+ yout = d + e * xref + f * yref
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = x0 - b * xref0 - c * yref0 = xshift
+ d = y0 - e * xref0 - f * yref0 = xshift
+.fi
+
+Xref0, yref0, x0, and
+y0 are the origins of the reference and output coordinate systems
+respectively. xmag and ymag are the x and y scale factors in output units
+per reference unit and xrotation and yrotation are the rotation angles measured
+counter-clockwise of the x and y axes.
+
+The linear portion of the GEOMAP transformation may be altered after the fact
+by setting some or all of the parameters \fIxref\fR, \fIyref\fR, \fIxout\fR,
+\fIyout\fR, \fIxshift\fR, \fIyshift\fR, \fIxmag\fR, \fIymag\fR, \fIxrotation\fR,
+and \fIyrotation\fR. If defined these parameters will replace the corresponding
+values in the GEOMAP database file.
+Xref, yref, xshift, yshift, xout and yout can be used to redefine the shift
+where xshift and yshift take precedence over xref, yref, xout and yout.
+Xmag, and ymag can be used to reset the scale of the transformation.
+Xrotation and yrotation can be used to redefine the orientation of the
+transformation. Note that xrotation and yrotation are interpreted as
+the rotation of the coordinate axes not the coordinates.
+The default values of these parameters are.
+
+.nf
+ xref = (xmin + xmax) / 2.0
+ yref = (ymin + ymax) / 2.0
+ xout = f (xref,yref)
+ yout = g (xref,yref)
+ xshift = xshift (database) = xout - f(xref,yref)
+ yshift = yshift (database) = yout - g(xref,yref)
+ xmag = xmag (database)
+ ymag = ymag (database)
+ xrotation = xrotation (database)
+ yrotation = yrotation (database)
+.fi
+
+If the GEOMAP database is undefined then GEOXYTRAN performs a linear
+transformation on the input coordinates using the parameters
+\fIxref\fR, \fIyref\fR, \fIxmag\fR, \fIymag\fR, \fIxrotation\fR,
+\fIyrotation\fR, \fIxout\fR, \fIyout\fR, \fIxshift\fR, and
+\fIyshift\fR as shown below. Note that in this case xrotation and
+yrotation are interpreted as the rotation of the coordinates
+themselves not the coordinate axes.
+
+.nf
+ xout = a + b * xref + c * yref
+ yout = d + e * xref + f * yref
+ b = xmag * cos (xrotation)
+ c = -ymag * sin (yrotation)
+ e = xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xo - b * xref0 - c * yref0 = xshift
+ d = yo - e * xref0 - f * yref0 = xshift
+.fi
+
+
+.ih
+Forward vs. Backward Transformations
+
+The transformation direction is specified by the \fIdirection\fR parameter
+which may take the values "forward" or "backward". The forward transformation
+is a direct evaluation of the database solution. The backward
+transformation is an iterative evaluation to obtain the coordinate which
+evaluates to the desired coordinate.
+
+When the same solution is used with \fBgeotran\fR to transform an image
+to another image matching the "reference" image is needed to obtain
+coordinates in the transformed image. This is because the transformation
+is produced with \fBgeomap\fR to map "reference" coordinates to the
+image which is subsequently transformed. Therefore, if you have coordinates
+in the image which has been transformed then you should use the "backward"
+transformation to get coordinates for the transformed image. But if you
+have standard coordinates from the reference image being matched then you
+would use the "forward" transformation. If you are not sure then you can
+use \fBtvmark\fR to overlay the results to find which direction produces
+the desired coordinates.
+
+Because the backward transformation is performed iteratively it can be
+slow. If higher speeds are desired, such as when evaluating a very
+large number of coordinates, one might create a transformation solution
+that can be evaluated in the forward direction. This is done by
+using \fBgeomap\fR with the reference and target coordinates reversed.
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+EXAMPLES
+
+.nf
+1. Compute the transformation from the reference system to the output
+system and then evaluate the transformation for both the input list and
+the list of unknowns.
+
+ cl> type rtran
+
+ 1.0000 1.0000 184.1445 -153.0376
+ 512.0000 1.0000 684.0376 184.1445
+ 512.0000 512.0000 346.8555 684.0376
+ 1.0000 512.0000 -153.0380 346.8555
+
+ cl> geomap rtran rtran.db 1.0 512.0 1.0 512.0 intera-
+
+ cl> type rtran.db
+
+ # Tue 14:53:36 18-Apr-95
+ begin rtran
+ output rtran.db
+ xrefmean 256.5
+ yrefmean 256.5
+ xmean 265.4999
+ ymean 265.5
+ xshift 183.826
+ yshift -154.6757
+ xmag 1.180001
+ ymag 1.179999
+ xrotation 326.
+ yrotation 326.
+ surface1 11
+ 3. 3.
+ 2. 2.
+ 2. 2.
+ 0. 0.
+ 1. 1.
+ 512. 512.
+ 1. 1.
+ 512. 512.
+ 183.826 -154.6757
+ 0.9782647 0.6598474
+ -0.6598479 0.9782643
+ surface2 0
+
+ cl> geoxytran rtran STDOUT rtran.db rtran
+
+ 184.1444 -153.038 184.1445 -153.0376
+ 684.0377 184.1444 684.0376 184.1445
+ 346.8554 684.0375 346.8555 684.0376
+ -153.038 346.8555 -153.038 346.8555
+
+ cl> geoxytran unknowns unknowns.tran rtran.db rtran
+
+
+2. Evaluate the backward transformation to take coordinates from the
+output system to the reference system. In this example we use the
+output of the first example to illustrate getting back the coordinates
+used in the original geomap input.
+
+ cl> geoxytran rtran STDOUT rtran.db rtran dir=forward |\
+ >>> geoxytran STDIN STDOUT rtran.db rtran dir=backward
+ 0.999798 0.9997257 184.1445 -153.0376
+ 512. 0.9999674 684.0376 184.1445
+ 512. 512. 346.8555 684.0376
+ 0.999918 512.0001 -153.0380 346.8555
+
+
+3. Evaluate the transform computed in example 1 for the same list of
+unknowns but modify the transformation slightly by setting xmag
+and ymag to 1.18 and 1.18 exactly.
+
+ cl> geoxytran unknowns unknowns.tran rtran.db rtran xmag=1.18 \
+ ymag=1.18
+
+
+4. Evaluate the same transformation for the same unknowns as before
+using the linear transformation parameters not the transform computed
+by geomap. Note that the angle is the negative of the one defined
+in the database file.
+
+ cl> geoxytran unknowns unknowns.tran "" xmag=1.18 ymag=1.18 \
+ xrot=34 yrot=34 xshift=183.826 yshift=-154.6757
+.fi
+
+.ih
+BUGS
+
+.ih
+SEE ALSO
+geomap, lists.lintran, geotran, gregister
+.endhelp
diff --git a/pkg/images/immatch/doc/gregister.hlp b/pkg/images/immatch/doc/gregister.hlp
new file mode 100644
index 00000000..73dff3d4
--- /dev/null
+++ b/pkg/images/immatch/doc/gregister.hlp
@@ -0,0 +1,265 @@
+.help gregister Dec98 images.immatch
+.ih
+NAME
+gregister -- transform a list of images from one coordinate system to another
+.ih
+USAGE
+gregister input output database transforms
+.ih
+PARAMETERS
+.ls input
+List of images to be transformed.
+.le
+.ls output
+List of output images.
+.le
+.ls database
+The name of the text file database produced by GEOMAP containing the coordinate
+transformation(s).
+.le
+.ls transforms
+The list of the database record(s) containing the transformations.
+The number of transforms must be 1 or the same as the number of input
+images. Transforms is usually the name of the
+text file input to GEOMAP which lists the reference and input
+coordinates of the control points.
+.le
+.ls geometry = "geometric"
+The type of geometry to be applied: The choices are:
+.ls linear
+The linear part, shifts, scales and rotations are computed.
+.le
+.ls geometric
+The full transformation is computed.
+.le
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum x and y reference values of the output image.
+Xmin, xmax, ymin and ymax default to minimum and maximum values set in GEOMAP,
+and may not extend beyond the bounds of those parameters.
+.le
+.ls xscale = 1.0, yscale = 1.0
+The output x and y scales in units of reference x and y
+units per pixel, e.g "/ pixel or Angstroms / pixel if the reference
+coordinates
+are arc-seconds or Angstroms. If the reference coordinates are in pixels
+then xscale and yscale should be 1.0 to preserve the scale of the reference
+image. The default is set for pixel coordinates.
+If xscale and yscale are undefined (INDEF), xscale and yscale default to the
+range of the reference coordinates over the range in pixels.
+Xscale and yscale override the values of ncols and nlines.
+.le
+.ls ncols = INDEF, nlines = INDEF
+The number of columns and lines in the output image. Ncols and nlines default
+to the size of the input image. If xscale or yscale are defined ncols or nlines
+are overridden.
+.le
+.ls xsample = 1.0, ysample = 1.0
+The coordinate surface subsampling factor. The coordinate surfaces are
+evaluated at every xsample-th pixel in x and every ysample-th pixel in y.
+Transformed coordinates at intermediate pixel values are determined by
+bilinear interpolation in the coordinate surfaces.
+.le
+.ls interpolant = "linear"
+The choices are the following.
+.ls nearest
+Nearest neighbor.
+.le
+.ls linear
+Bilinear interpolation in x and y.
+.le
+.ls poly3
+Third order polynomial in x and y.
+.le
+.ls poly5
+Fifth order polynomial in x and y.
+.le
+.ls spline3
+Bicubic spline.
+.le
+.ls sinc
+2D sinc interpolation. Users can specify the sinc interpolant width by
+appending a width value to the interpolant string, e.g. sinc51 specifies
+a 51 by 51 pixel wide sinc interpolant. The sinc width will be rounded up to
+the nearest odd number. The default sinc width is 31 by 31.
+.le
+.ls lsinc
+Look-up table sinc interpolation. Users can specify the look-up table sinc
+interpolant width by appending a width value to the interpolant string, e.g.
+lsinc51 specifies a 51 by 51 pixel wide look-up table sinc interpolant. The user
+supplied sinc width will be rounded up to the nearest odd number. The default
+sinc width is 31 by 31 pixels. Users can specify the resolution of the lookup
+table sinc by appending the look-up table size in square brackets to the
+interpolant string, e.g. lsinc51[20] specifies a 20 by 20 element sinc
+look-up table interpolant with a pixel resolution of 0.05 pixels in x and y.
+The default look-up table size and resolution are 20 by 20 and 0.05 pixels
+in x and y respectively.
+.le
+.ls drizzle
+2D drizzle resampling. Users can specify the drizzle pixel fraction in x and y
+by appending a value between 0.0 and 1.0 in square brackets to the
+interpolant string, e.g. drizzle[0.5]. The default value is 1.0.
+The value 0.0 is increased internally to 0.001. Drizzle resampling
+with a pixel fraction of 1.0 in x and y is equivalent to fractional pixel
+rotated block summing (fluxconserve = yes) or averaging (flux_conserve = no) if
+xmag and ymag are > 1.0.
+.le
+.le
+.ls boundary = "nearest"
+The boundary extension choices are:
+.ls nearest
+Use the value of the nearest boundary pixel.
+.le
+.ls constant
+Use a constant value.
+.le
+.ls reflect
+Generate value by reflecting about the boundary.
+.le
+.ls wrap
+Generate a value by wrapping around to the opposite side of the image.
+.le
+.le
+.ls constant = 0.
+The value of the constant for boundary extension.
+.le
+.ls fluxconserve = yes
+Preserve the total image flux. The output pixel values are multiplied by
+the Jacobian of the coordinate transformation.
+.le
+.ls nxblock = 512, nyblock = 512
+If the dimensions of the output image are less than nxblock and nyblock
+then the entire image is transformed at once. Otherwise blocks of size
+nxblock by nyblock are transformed one at a time.
+.le
+.ls verbose = yes
+Print messages about the progress of the task ?
+.le
+.ih
+DESCRIPTION
+
+GREGISTER corrects an image for geometric distortion using the coordinate
+transformation computed by GEOMAP. The transformation is stored as the
+coefficients of a polynomial surface in record \fItransforms\fR,
+in the text file \fIdatabase\fR.
+The coordinate surface is sampled at every \fIxsample\fR and \fIysample\fR
+pixel in x and y.
+The transformed coordinates at intermediate pixel values are
+determined by bilinear interpolation in the coordinate surface. If
+\fIxsample\fR and \fIysample\fR = 1, the coordinate
+surface is evaluated at every pixel. Use of \fIxsample\fR and \fIysample\fR
+are strongly recommended for large images and high order coordinate
+surfaces in order to reduce the execution time.
+
+\fIXmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR define the range of
+reference coordinates represented in the output picture. These numbers
+default to the minimum and maximum x and y reference values used by GEOMAP,
+and may not exceed these values.
+The scale and size of the output picture is determined as follows.
+
+.nf
+ ncols = ncols(input)
+ if (xscale == INDEF)
+ xscale = (xmax - xmin ) / (ncols - 1)
+ else
+ ncols = (xmax - xmin) / xscale + 1
+
+ nlines = nlines(input)
+ if (yscale == INDEF)
+ yscale = (ymax - ymin ) / (nlines - 1)
+ else
+ nlines = (ymax - ymin) / yscale + 1
+.fi
+
+The output image gray levels are determined by interpolating in the input
+image at the positions of the transformed output pixels. If the
+\fIfluxconserve\fR switch is set the output pixel values are multiplied by
+the Jacobian of the transformation. GREGISTER uses the routines in the
+2-D interpolation package.
+
+The output image is computed in \fInxblock\fR by \fInyblock\fR pixel sections.
+If possible users should set these numbers to values larger than the dimensions
+of the output image, in order to minimize the number of disk reads and writes
+required to compute the output image. If this is not feasible and the image
+rotation is small users should set nxblock to be greater than the number of
+columns in the output image, and nyblock to be as large as machine memory
+will permit.
+
+If the environment variable \fInomwcs\fR is "no" then the world coordinate
+system of the input image is modified in the output image to reflect the
+effects of the \fIlinear\fR portion of the registration operation.
+Support does not yet exist in the IRAF world coordinate system interface
+for the higher order distortion corrections that GREGISTER is capable
+of performing.
+
+.ih
+TIMINGS
+It requires approximately 70 and 290 cpu seconds to correct a 512 by 512
+square image for geometric distortion using a low order coordinate surface
+and bilinear and biquintic interpolation respectively (Vax 11/750 far).
+
+.ih
+EXAMPLES
+.ls 4 1.
+Transform an image to the reference coordinate system of a 512 by 512 pixel
+square image. The output image will have the same scale and size as the
+reference image if the reference coordinates are in pixels.
+
+.nf
+cl> geomap coords database 1.0 512.0 1.0 512.0
+cl> gregister input output database coords
+.fi
+.le
+.ls 4 2.
+Repeat the previous example but rescale the output image. The scale of the
+output image will be 2.5 reference units per pixel and its size will be
+determined by the xmin, xmax, ymin, ymax parameters (1.0, 512.0, 1.0, 512.0).
+
+.nf
+cl> geomap coords database 1.0 512.0 1.0 512.0
+cl> gregister input output database coords xscale=2.5 yscale=2.5
+.fi
+.le
+.ls 4 3.
+Correct an image for 3rd order geometric distortion using an output scale of 2
+reference units per pixel unit and bicubic spline interpolation with no flux
+correction.
+
+.nf
+cl> geomap coords database 1.0 512.0 1.0 512.0 xxorder=4 xyorder=4 \
+xxterms=yes yxorder=4 yyorder=4 yxterms=yes
+cl> gregister input output database coords xscale=2. yscale=2. \
+>>> inter=spline3 flux-
+.fi
+.le
+.ls 4 4.
+Transform three images using 3 different transformation records stored
+in the database file.
+
+.nf
+cl> geomap coord1,coord2,coord3 database 1. 512. 1. 512.
+cl> gregister im1,im2,im3 imout1,imout2,imout3 database \
+>>> coord1,coord2,coords3
+.fi
+.le
+.ls 4 5.
+Repeat the above example using the textfiles inlist, outlist, reclist which
+contain the list of input images, list of output images and list of coordinate
+files respectively.
+
+.nf
+cl> geomap @reclist database 1. 512. 1. 512.
+cl> gregister @inlist @outlist database @reclist
+.fi
+.le
+
+.ih
+BUGS
+Support does yet exist in the IRAF world coordinate system interface
+for the higher order distortion corrections that GREGISTER is capable
+of performing.
+
+.ih
+SEE ALSO
+imshift, magnify, rotate, imlintran, geomap, geotran, geoxytran
+.endhelp
diff --git a/pkg/images/immatch/doc/imalign.hlp b/pkg/images/immatch/doc/imalign.hlp
new file mode 100644
index 00000000..c63be5bc
--- /dev/null
+++ b/pkg/images/immatch/doc/imalign.hlp
@@ -0,0 +1,316 @@
+.help imalign Feb90 images.immatch
+.ih
+NAME
+imalign -- register a list of images by computing relative object shifts
+.ih
+USAGE
+imalign input reference coords output
+.ih
+PARAMETERS
+.ls input
+The input images to be shifted and trimmed. The input image list should
+contain the reference image so that its borders are
+used in the computation of the overlap region.
+.le
+.ls reference
+The reference image to which the input images will be aligned.
+.le
+.ls coords
+A text file containing the reference image coordinates of the registration
+objects to be centered in each image, one object per line with the x and y
+coordinates in columns one and two respectively.
+.le
+.ls output
+The output images.
+.le
+.ls shifts = ""
+A text file containing the initial estimate for each image of the
+shift in each axis relative to the reference image. These
+estimates are used to modify the coordinates of the registration
+objects prior to centering. The format of the file is one image per
+line with the x and y shifts in columns one and two respectively.
+The sense of the shifts is such that: \fIXshift=Xref-Xin\fR and
+\fBYshift=Yref-Yin\fR. If \fIshifts\fR is null, a coarse centering
+pass will be made to attempt to determine the initial shifts.
+.le
+.ls boxsize = 7
+The size in pixels of the box to use for the final centering, during
+which all the sources in \fIcoords\fR are recentered in each image
+using the initial estimate of the relative shift for each image.
+Care should be taken to choose an appropriate value for this parameter,
+since it is highly data dependent.
+.le
+.ls bigbox = 11
+The size in pixels of the box to use for coarse centering. The coarse
+pass through the centering algorithm is made with the box centered at
+the nominal position of the first source in the coordinate list.
+Coarse centering is performed only if the shifts file is undefined.
+Care should be taken to choose an appropriate value for this parameter,
+since it is highly data dependent. Large values should be suspect until
+the final results are checked to see that the centering did not converge
+on the wrong coordinates, although the usual result for an inappropriate
+\fIbigbox\fR size is that the algorithm fails to converge and the task
+aborts.
+.le
+.ls negative = no
+Are the features negative ?
+.le
+.ls background = INDEF
+The absolute reference level for the marginal centroid calculation.
+If background is INDEF, this is set to the mean value (between the
+thresholds) of the individual sources.
+.le
+.ls lower = INDEF
+The lower threshold for the data. Individual pixels less than this
+value will be given zero weight in the centroids.
+.le
+.ls upper = INDEF
+The upper threshold for the data. Individual pixels greater than this
+value will be given zero weight in the centroids.
+.le
+.ls niterate = 3
+The maximum number of centering iterations to perform. The centering
+will halt when this limit is reached or when the desired Itolerance
+is achieved.
+.le
+.ls tolerance = 0
+The tolerance for convergence of the centering algorithm. This is the
+integral shift of the centering box from one iteration to the next.
+.le
+.ls maxshift = INDEFR
+The maximum permitted difference between the predicted shift and the
+the computed shift for each object. Objects with shifts greater than
+maxshift are ignored. If maxshift is undefined no shift checking is done.
+.le
+.ls shiftimages = yes
+If shiftimages is yes, the IMSHIFT task will be used to align the
+images. If shiftimages is no, the images will not be aligned, but
+the coordinates will still be centered.
+.le
+.ls interp_type = "spline3"
+The interpolation function used by the IMSHIFT task.
+.le
+.ls boundary_type = "constant"
+The boundary extension type used by the IMSHIFT task.
+.le
+.ls constant = 0.
+The constant used by the IMSHIFT task if \fIboundary_type\fR is "constant".
+.le
+.ls trimimages = yes
+If trimimages is yes, the output images will be trimmed to
+include only the region over which they all overlap. The
+trim section that is actually used may differ slightly from that
+reported by IMCENTROID, due to a correction applied to compensate for
+the boundary extension "contamination" near the edges of the images.
+.le
+.ls verbose = yes
+Print the centers, shifts, and trim section?
+.le
+.ih
+DESCRIPTION
+IMALIGN measures the X and Y axis shifts between a list of input images
+\fIinput\fR and a reference image \fIreference\fR, registers the
+input images to the reference image using the computed shifts,
+and trims the input images to a common overlap region.
+The task is meant to address the class of two dimensional image
+registration problems in which the images have the same pixel scale,
+are shifted relative to each other by simple x and y translations, and contain
+enough high signal / noise, pointlike sources in common to compute good
+average positions. The basic operation of the task is to find centers
+for the list of registration objects or features in the coordinate
+frame of each image and then to subtract the corresponding centers
+found in the reference image. The shifts of the registration objects
+are averaged for each image.
+
+IMALIGN is a simple script front end for IMCENTROID, which computes the
+shifts, IMSHIFT, which shifts the images, and
+IMCOPY, which performs the trimming.
+
+A list of the X and Y coordinates of the registration objects should be
+provided via the \fIcoords\fR parameter. The registration objects do not
+all have to be common to each frame; only that subset of the
+objects that is contained within the bounds of a given image will be
+centered. Only the objects that are common to both the given image and
+the reference will be used to calculate the shifts. The coordinates
+must be measured in the frame of the reference image. If coarse
+centering is to be done, which is to say, if no \fIshifts\fR file is
+provided, then the first registration source should be separated from
+other sources by at least the maximum expected relative shift.
+
+An initial estimate of the shifts between each of the input images and
+the reference image is required for the centering algorithm (a marginal
+centroid) to work. This estimate can be explicitly supplied in the file
+\fIshifts\fR (\fIXshift=Xref-Xin\fR and \fIYshift=Yref-Yin\fR) or can
+be generated from the images by measuring the relative shift of the
+first source listed in the coords file for each image. This coarse
+centering pass requires that the first source be detached from other
+sources and from the border of each image, by a distance that is at
+least the maximum shift between the reference and input image. This
+source should be pointlike and have a high signal to noise ratio. The
+value of the \fIbigbox\fR parameter should be chosen to include the
+location of the source in each of the images to be aligned while
+excluding other sources. Large values of \fIbigbox\fR should be held
+suspect until the final convergence of the centering algorithm is
+verified, although given a small value for the \fItolerance\fR, the
+quality of the final centers is independent of the estimate for the
+initial shifts. Better convergence may also be obtained by increasing
+the \fIniterate\fR parameter, although the default value of three
+should work for most cases. \fINiterate\fR should be kept small to
+avoid runaway.
+
+The \fIboxsize\fR parameter controls the size of the centering box for
+the fine centering passes and should be chosen so as to exclude sky
+background and other sources while including the wings of the point
+spread function. The sense of the shifts that are calculated is
+consistent with the file supplied to the \fIshifts\fR parameter and
+with that used with the IMSHIFT task.
+
+If \fIshiftimages\fR is yes the images will actually be shifted using
+the IMSHIFT task. Note that if \fIinterp_type\fR is "nearest" the
+effect on the images is the same as if the shifts were rounded to
+integral values. In this case, the pixels will be shifted without
+interpolation. This can be used for data in which it is more important
+to preserve the pixel values than it is to achieve perfect
+registration.
+
+If \fItrimimages\fR is yes, the output images will be trimmed to
+include only the region over which they all overlap. The trim section
+that is actually used may differ slightly from that reported by
+IMCENTROID. A one or two pixel correction may be applied to each edge
+to compensate for the boundary extension "contamination" due to
+multi-pixel (e.g., \fIinterp_type\fR = poly5) interpolation near the
+edges of the images.
+
+IMALIGN may be used with a set of \fIimages\fR which vary in size.
+This can result in vignetting of the calculated overlap region because
+of the nature of the IMSHIFT task to preserve the size of an input
+image. To visualize this, imagine a large reference image and a single
+small image to be aligned to it, both containing the same registration
+object which is at the center of each image. IMALIGN will cause the
+small image to be shifted such that the object is positioned at the same
+pixel location as in the reference. In performing the shift, a large
+fraction of the area of the small image may be shifted outside of its
+own borders, whereas the physical overlap of the large and small images
+includes ALL of the pixels of the small image. In the case of such
+vignetting, IMALIGN will print a warning message and refuse to proceed
+with the trimming although the vignetting will occur whether or not the
+images are trimmed. Note that the vignetting will not occur if the
+small image is used as the \fIreference\fR.
+
+The vignetting message may also be printed if the \fIimages\fR are all
+the same size but the \fIreference\fR is not included in the list.
+This will occur if the sense of the measured shifts in a coordinate are
+all positive or all negative since in this case the border of the
+\fIreference\fR would have provided one of the limits to the trim
+section. The reality of this vignetting depends on your point of view.
+
+Trimming will also not be performed if the entire overlap region vanishes.
+
+Note that many of these difficulties are due to the intrinsically fuzzy
+nature of the process of image registration. This all leads to a few
+"rules of thumb":
+
+.nf
+ o Include the reference image in the input image list
+
+ o Use the smallest image as the reference image
+
+ o Choose the reference image such that the input images are
+ scattered to either side in the shifts in each axis
+
+ o Align images that are the same size, OR
+
+ o Pad dissimilar sized images with blanks to
+ the largest size and disable trimming
+.fi
+.ih
+CENTERING ALGORITHM
+The algorithm is a "marginal" centroid in which the fit for each axis
+is performed separately upon a vector created by collapsing the
+centering box perpendicular to that axis. The centroid is calculated
+with respect to the level specified by \fIbackground\fR. If
+\fIbackground\fR is INDEF, the reference level for each source in each
+image is the local mean for those pixels that lie between the
+\fIlower\fR and \fIupper\fR thresholds. The thresholds are set to the
+local data minimum or maximum if \fIlower\fR or \fIupper\fR,
+respectively, are INDEF. If \fInegative\fR is yes, than the marginal
+vector will be inverted before being passed to the centroid algorithm.
+
+The maximum number of centering iterations and the tolerance for
+convergence are controlled by \fIniterate\fR and \fItolerance\fR. Note
+that the tolerance is an integer value that represents the maximum
+movement of the centering box between two successive iterations. The
+default value of 0 requires that the centroid lie within the center
+pixel of the centering box which is \fIboxsize\fR in extent (note that
+\fIboxsize\fR must be an odd number). This should normally be the case
+for bright, circularly symmetric point sources in images with a flat
+sky background. If the registration sources are not circular symmetric
+try increasing the tolerance gingerly. A sky level that varies across
+the image should be removed before processing. The centering and
+calculation of the shifts may be performed with \fIshiftimages\fR = no
+(or directly with IMCENTROID) and the calculated shifts applied to the
+images directly with IMSHIFT.
+
+.ih
+EXAMPLES
+1. Align three images to the first using the list of registration star
+coordinates in the file "x1.coords".
+
+.nf
+ cl> imalign x1,x2,x3 x1 x1.coords x1.out,x2.out,x3.out
+.fi
+
+2. Align a list of images contained in the file "imlist", overwriting the
+original images with the shifted and trimmed images:
+
+.nf
+ cl> imalign @imlist x1 x1.coords @imlist
+.fi
+
+3. Align the images leaving the output images the same size as the input
+images:
+
+.nf
+ cl> imalign @imlist x1 x1.coords @outlist trimimages-
+.fi
+
+4. Perform the centering but not the shifts:
+
+.nf
+ cl> imalign @imlist x1 x1.coords shiftimages-
+.fi
+
+5. Perform the centering, but don't calculate the shifts at all,
+and don't shift the image.
+
+.nf
+ pr> imalign @imlist "" x1.coords shiftimages-
+.fi
+
+.ih
+BUGS
+The images being shifted must be in the current directory.
+
+The coarse centering portion of the algorithm can be fooled if the
+first source on the list is not well separated from other sources, or
+if the first source has a low signal to noise ratio, or if there is a
+complicated shape to the background.
+
+The task can produce output images that do not contain the entire
+overlap region. This can only occur if the images are of varying sizes.
+This behavior is caused by the action of the IMSHIFT task to preserve the
+size of an input image, thus implicitly "trimming" the image. A work
+around is to use IMCOPY to place the images into subsections of blank
+images that are the size (in each dimension) of the largest image(s)
+and use IMALIGN with \fItrimimages\fR set to no. The borders of the output
+images can be trimmed manually. This is discussed above in more detail.
+
+If \fIimages\fR does not contain the \fIreference\fR and \fItrimimages\fR
+is set to yes then the set of shifted and trimmed images may no longer
+be aligned to the reference. This occurs because any place holder
+pixels at the bottom and left edges of the images will be trimmed off.
+This is also discussed above.
+.ih
+SEE ALSO
+imcentroid, center, imshift, geomap, geotran
+.endhelp
diff --git a/pkg/images/immatch/doc/imcentroid.hlp b/pkg/images/immatch/doc/imcentroid.hlp
new file mode 100644
index 00000000..c284d9be
--- /dev/null
+++ b/pkg/images/immatch/doc/imcentroid.hlp
@@ -0,0 +1,257 @@
+.help imcentroid Jan97 images.immatch
+.ih
+NAME
+imcentroid -- center sources in images, optionally find shifts
+
+.ih
+USAGE
+imcentroid input reference coords
+
+.ih
+PARAMETERS
+
+.ls input
+The list of images within which sources are to be centered. If a
+\fIreference\fR image is specified, imcentroid will calculate the mean
+X and Y shifts between the centered sources within each image and those
+same sources within the reference image. The input image list
+should normally include the reference image so that its borders are
+used in the calculation of the overlap region.
+.le
+.ls reference = ""
+The reference image to which the input images will be aligned. If
+a reference image is specified the mean X and Y shifts between each of
+the input images and the reference image will be calculated, otherwise
+only the centers for the individual sources will be reported.
+.le
+.ls coords
+A text file containing the coordinates of the registration objects to
+be centered in each image, one object per line with the x and y
+coordinates in columns one and two respectively. These coordinates
+should be measured in the frame of the reference image.
+.le
+.ls shifts = ""
+A text file containing the initial estimate for each image of the
+shift in each axis relative to the reference image. These
+estimates are used to modify the coordinates of the registration
+objects prior to centering. The format of the file is one image per
+line with the fractional x and y shifts in columns one and two
+respectively. The sense of the shifts is such that:
+Xshift =Xref - Xin and shift= Yref - Yin. If shifts is undefined,
+a coarse centering pass will be made to attempt to determine
+the initial shifts.
+.le
+.ls boxsize = 7
+The size in pixels of the box to use for the final centering, during
+which all the sources in the coords file are recentered in each image
+using the initial estimate of the relative shift for each image.
+Care should be taken to choose an appropriate value for this parameter,
+since it is highly data dependent.
+.le
+.ls bigbox = 11
+The size in pixels of the box to use for coarse centering. The coarse
+pass through the centering algorithm is made with the box centered at
+the nominal position of the first source in the coordinate list.
+Coarse centering is performed only if the shifts file is undefined.
+Care should be taken to choose an appropriate value for this parameter,
+since it is highly data dependent. Large value should be suspect until
+the final results are checked to see that the centering did not converge
+on the wrong coordinates, although the usual result for an inappropriate
+bigbox size is that the algorithm fails to converge and the task
+aborts.
+.le
+.ls negative = no
+Are the features negative ?
+.le
+.ls background = INDEF
+The absolute reference level for the marginal centroid calculation.
+If background is INDEF, this is set to the mean value (between the
+thresholds) of the individual sources.
+.le
+.ls lower = INDEF
+The lower threshold for the data. Individual pixels less than this
+value will be given zero weight in the centroids.
+.le
+.ls upper = INDEF
+The upper threshold for the data. Individual pixels greater than this
+value will be given zero weight in the centroids.
+.le
+.ls niterate = 3
+The maximum number of centering iterations to perform. The centering
+will halt when this limit is reached or when the desired tolerance
+is achieved.
+.le
+.ls tolerance = 0
+The tolerance for convergence of the centering algorithm. This is the
+integral shift of the centering box from one iteration to the next.
+.le
+.ls maxshift = INDEFR
+The maximum permitted difference between the predicted shift and the
+the computed shift for each object. Objects with shifts greater than
+maxshift are ignored. If maxshift is undefined no shift checking is done.
+.le
+.ls verbose = yes
+Print the centers for the individual objects ? If verbose is no
+only the shifts relative to the reference coordinates will be reported.
+If no reference image is supplied, verbose is automatically set to yes.
+.le
+
+.ih
+DESCRIPTION
+
+IMCENTROID measures the X and Y coordinates of a list of sources in a
+list of images and finds the mean X and Y shifts between the input
+images \fIinput\fR and a \fIreference\fR image, where the shifts are
+defined as the shifts that should be added to the input image coordinates to
+convert them into the reference coordinates. The task is meant to
+address the class of two dimensional image registration problems in
+which the images have the same pixel scale, are shifted relative to
+each other by simple translations in each axis, and contain enough high
+signal-to-noise, pointlike sources in common to form good average
+positions. The basic operation of the task is to find centers for the
+list of registration objects in the coordinate frame of each image and
+then to subtract the corresponding centers found in the reference
+image. The shifts of the objects are averaged for each image.
+
+A list of the X and Y coordinates of the registration objects should be
+provided in the coordinates file \fIcoords\fR. The registration objects do not
+all have to be common to each frame, rather only that subset of the
+objects that is contained within the bounds of a given image will be
+centered. Only the objects that are common to both the given image and
+the reference will be used to calculate the shifts. The coordinates
+should be measured in the frame of the reference image\fIreference\fR.
+If coarse centering is to be done, which is to say, if no \fIshifts\fR file is
+provided, then the first registration source should be separated from
+other sources by at least the maximum expected relative shift.
+
+An initial estimate of the shifts between each of the input images
+\fIinput\fR and the reference image \fIreference\fR is required for the
+centering algorithm (a marginal centroid) to work. This estimate can be
+explicitly supplied in the text file \fIshifts\fR where Xshift = Xref -Xin
+and Yshift = Yref -Y in, or can be generated from the images by measuring
+the relative shift of the first source listed in the coordinates file
+\fIcoords\fR for each input image. This coarse
+centering pass requires that the first source be detached from other
+sources and from the border of each image by a distance that is at
+least the maximum shift between the reference and input image. This
+source should be pointlike and have a high signal to noise ratio. The
+value of the \fIbigbox\fR parameter should be chosen to include the
+location of the source in each of the images to be aligned while
+excluding other sources. Large values of \fIbigbox\fR should be held
+suspect until the final convergence of the centering algorithm is
+verified, although given a small value for the \fItolerance\fR, the
+quality of the final centers is independent of the estimate for the
+initial shifts. Better convergence may also be obtained by increasing
+the \fIniterate\fR parameter, although the default value of three
+should work for most cases. \fINiterate\fR should be kept small to
+avoid runaway.
+
+The \fIboxsize\fR parameter controls the size of the centering box for
+the fine centering pass and should be chosen so as to exclude sky
+background and other sources while including the wings of the point
+spread function. The sense of the shifts that are calculated is
+consistent with the file supplied to the \fIshifts\fR parameter and
+with that used with the IMSHIFT task.
+
+IMCENTROID may be used with a set of input images which vary in size.
+This can result in vignetting of the calculated overlap region because
+of the nature of tasks such as IMSHIFT to preserve the size of an input
+image. To visualize this, imagine a large reference image and a single
+small image to be aligned to it, both containing the same registration
+object which is at the center of each image. IMCENTROID will cause the
+coordinate system of the small image to be shifted such that the object
+will be positioned at the same pixel location as in the reference. If
+the shift is performed, a large fraction of the area of the small image
+may be shifted outside of its own borders, whereas the physical overlap
+of the large and small images includes ALL of the pixels of the small
+image. In the case of such vignetting, IMCENTROID will print a warning
+message and both the vignetted and unvignetted trim sections. Note
+that the vignetting will not occur if the small image is used as the
+reference image.
+
+The vignetting message may also be printed if the input images are all
+the same size but the reference image is not included in the list.
+This will occur if the sense of the measured shifts in a coordinate are
+all positive or all negative since in this case the border of the
+reference image would have provided one of the limits to the trim
+section. The reality of this vignetting depends on your point of view.
+
+Note that many of these difficulties are due to the intrinsically fuzzy
+nature of the process of image registration. This all leads to a few
+guidelines:
+
+.nf
+ o Include the reference image in the input image list
+
+ o Use the smallest image as the reference image
+
+ o Choose the reference image such that the input images
+ are scattered to either side in the shifts in each axis
+
+ o Align images that are the same size, OR
+
+ o Pad dissimilar sized images with blanks to the largest size
+.fi
+
+.ih
+CENTERING ALGORITHM
+
+The algorithm is a "marginal" centroid in which the fit for each axis
+is performed separately upon a vector created by collapsing the
+centering box perpendicular to that axis. The centroid is calculated
+with respect to the level specified by \fIbackground\fR. If
+\fIbackground\fR is INDEF, the reference level for each source in each
+image is the local mean for those pixels that lie between the
+\fIlower\fR and \fIupper\fR thresholds. The thresholds are set to the
+local data minimum or maximum if \fIlower\fR or \fIupper\fR,
+respectively, are INDEF. If \fInegative\fR is yes, than the marginal
+vector will be inverted before being passed to the centroid algorithm.
+
+The maximum number of centering iterations and the tolerance for
+convergence are controlled by \fIniterate\fR and \fItolerance\fR. Note
+that the tolerance is an integer value that represents the maximum
+movement of the centering box between two successive iterations. The
+default value of 0 requires that the centroid lie within the center
+pixel of the centering box which is \fIboxsize\fR in extent (note that
+\fIboxsize\fR must be an odd number). This should normally be the case
+for bright, circularly symmetric point sources in images with a flat
+sky background. If the registration sources are not circular symmetric
+try increasing the tolerance gingerly. If the sky background is not
+flat, but varies across the image, it can be removed before processing.
+
+.ih
+EXAMPLES
+
+1. Calculate the shifts between three images using the first image
+as a reference image and the list of registration star coordinates in
+the file "x1.coords".
+
+.nf
+ cl> imcentroid x1,x2,x3 x1 x1.coords
+.fi
+
+2. Calculate the shifts between a list of images contained in the file
+"imlist":
+
+.nf
+ pr> imcentroid @imlist x1 x1.coords
+.fi
+
+3. Perform the centering, but don't calculate the shifts, i.e., don't
+supply a reference image. Note that the \fIinput\fR list of shifts,
+or a coarse centering pass are still needed:
+
+.nf
+ pr> imcentroid @imlist "" x1.coords
+.fi
+
+.ih
+BUGS
+The coarse centering portion of the algorithm can be fooled if the
+first source on the list is not well separated from other sources, or
+if the first source has a low signal to noise ratio, or if there is a
+complicated shape to the background.
+.ih
+SEE ALSO
+imalign, imshift, xregister, geomap, geotran
+.endhelp
diff --git a/pkg/images/immatch/doc/imcombine.hlp b/pkg/images/immatch/doc/imcombine.hlp
new file mode 100644
index 00000000..720fe785
--- /dev/null
+++ b/pkg/images/immatch/doc/imcombine.hlp
@@ -0,0 +1,1471 @@
+.help imcombine Aug01 images.immatch
+.ih
+NAME
+imcombine -- Combine images using various algorithms
+.ih
+USAGE
+imcombine input output
+.ih
+PARAMETERS
+.ls input
+List of input images to combine. If the \fIproject\fR parameter is "no"
+then all input images must have the same dimensionality though they may
+be of different sizes. Otherwise each input image is handled separately
+and they may have different dimensionalities.
+.le
+
+
+When the \fIproject\fR parameter is "no" all the input images are combined
+into a single output file. In this case the following parameters specify
+only a single file name. Otherwise each input image is combined by
+projecting (combining across) the highest dimension to produce a lower
+dimensional image. For this type of combining there is one output for each
+input and so the following parameters specify matching lists.
+
+.ls output
+Output combined image(s). If there are fewer than 100 input images the
+names of the input images are recorded in header keywords IMCMBnnn.
+.le
+.ls headers = "" (optional)
+Optional output multiextension FITS file(s). The extensions are dataless
+headers from each input image.
+.le
+.ls bpmasks = "" (optional)
+Optional output bad pixel mask(s) with good values of 0 and bad values of
+1. Output pixels are marked as bad when no input pixels contributed to the
+output pixel. The file name is also added to the output image header under
+the keyword BPM.
+.le
+.ls rejmask = "" (optional)
+Optional output mask file(s) identifying rejected or excluded pixels. The
+pixel mask is the size of the output image but there is one extra dimension
+with length equal to the number of input images. Each element of the
+highest dimension is a mask corresponding to an input image with values of
+1 for rejected or excluded pixels and values of 0 for pixels which were
+used. The order of the masks is the order of the input images and image
+header keywords, indexed by the pixel coordinate of the highest dimension
+identify the input images. Note that the pixel positions are in the output
+pixel coordinate system.
+.le
+.ls nrejmasks = "" (optional)
+Optional output pixel mask(s) giving the number of input pixels rejected or
+excluded from the input images.
+.le
+.ls expmasks = "" (optional)
+Optional output exposure mask(s) giving the sum of the exposure values of
+the input images with non-zero weights that contributed to that pixel.
+Since masks are integer, the exposure values may be scaled to preserve
+dynamic range and fractional significance. The scaling values are given in
+the header under the keywords MASKSCAL and MASKZERO. Exposure values are
+computed from the mask values by scale * value + zero where scale is the
+value of the MASKSCAL keyword and zero is the value of the MASKZERO
+keyword.
+.le
+.ls sigma = "" (optional)
+Optional output sigma image(s). The sigma is the standard deviation,
+corrected for a finite population, of the input pixel values (excluding
+rejected pixels) about the output combined pixel values.
+.le
+
+.ls imcmb = "$I" (optional)
+A keyword in the input images that is copied
+to one of the IMCMBnnn keywords in the output image. A null string
+does not set the IMCMBnnn keywords nor deletes any existing keywords.
+Any other value will delete existing keywords before creating new ones.
+The special value "$I" specifies the basename of the input image name.
+If a keyword is specified that does not exist in the input image(s) then
+no ICMB keyword will be produced; it is not a error for the keyword to
+not exist.
+.le
+.ls logfile = "STDOUT" (optional)
+Optional output log file. If no file is specified then no log information is
+produced. The special filename "STDOUT" prints log information to the
+terminal.
+.le
+
+.ls combine = "average" (average|median|lmedian|sum|quadrature|nmodel)
+Type of combining operation performed on the final set of pixels (after
+offsetting, masking, thresholding, and rejection). The choices are:
+
+.nf
+ average - weighted average
+ median - median
+ lmedian - median except use the lower value if only two
+ sum - (weighted) sum
+ quadrature - weighted quadrature average
+ nmodel - weighted quadrature average of noise model values
+.fi
+
+The details of each choice is given in the DESCRIPTION.
+Note that if weights are used then the weighted "sum" is the same as
+the weighted "average" since the weights are normalized to unit total weight.
+The "lmedian" option is intended for minimizing the effects of cosmic rays
+when there are more than two images but some pixels may only have two
+contributing images. The "quadrature" and "nmodel" options are used
+for error propagation either with input sigma images (quadrature) or where the
+pixel sigmas may be computed by the noise model used by this task (nmodel).
+.le
+.ls reject = "none" (none|minmax|ccdclip|crreject|sigclip|avsigclip|pclip)
+Type of rejection operation performed on the pixels remaining after offsetting,
+masking and thresholding. The algorithms are described in the
+DESCRIPTION section. The rejection choices are:
+
+.nf
+ none - No rejection
+ minmax - Reject the nlow and nhigh pixels
+ ccdclip - Reject pixels using CCD noise parameters
+ crreject - Reject only positive pixels using CCD noise parameters
+ sigclip - Reject pixels using a sigma clipping algorithm
+ avsigclip - Reject pixels using an averaged sigma clipping algorithm
+ pclip - Reject pixels using sigma based on percentiles
+.fi
+
+.le
+.ls project = no
+Project (combine) across the highest dimension of the input images? If
+"no" then all the input images are combined to a single output image. If
+"yes" then the highest dimension elements of each input image are combined to
+an output image and optional pixel list and sigma images. Each element of
+the highest dimension may have a separate offset.
+.le
+.ls outtype = "real" (none|short|ushort|integer|long|real|double)
+Output image pixel datatype. The pixel datatypes are "double", "real",
+"long", "integer", unsigned short "ushort", and "short" with highest
+precedence first. If "none" is specified then the highest precedence
+datatype of the input images is used. When there is a mixture of
+short and unsigned short images the highest precedence become integer.
+The datatypes may be abbreviated to a single character.
+.le
+.ls outlimits = ""
+Output region limits specified as pairs of whitespace separated values.
+The first two numbers are the limits along the first output image dimension,
+the next two numbers are the limits along the second dimension, and so on.
+If the higher dimension limits are not specified they default to the full
+range. Therefore, if no limits are specified then the full output is
+created. Note that the output size is computed from all the input images
+including offsets if specified and the coordinates are relative to that
+size.
+.le
+.ls offsets = "none" (none|wcs|world|physical|grid|<filename>)
+Integer offsets to add to each image axes. The options are:
+.ls "none"
+No offsets are applied.
+.le
+.ls "wcs" or "world"
+The world coordinate system (wcs) in the image is used to derive the
+offsets. The nearest integer offset that matches the world coordinate
+at the center of the first input image is used.
+.le
+.ls "physical"
+The physical coordinate system defined by the IRAF LTM/LTV keywords
+define the offsets.
+.le
+.ls "grid"
+A uniform grid of offsets is specified by a string of the form
+
+.nf
+ grid [n1] [s1] [n2] [s2] ...
+.fi
+
+where ni is the number of images in dimension i and si is the step
+in dimension i. For example "grid 5 100 5 100" specifies a 5x5
+grid with origins offset by 100 pixels.
+.le
+.ls <filename>
+The offsets are given in the specified file. The file consists
+of one line per image with the offsets in each dimension forming the
+columns.
+.le
+.le
+.ls masktype = "none"
+Type of pixel masking to use. The choices are
+
+.nf
+ none - No pixel masking
+ goodvalue - good pixels defined by maskvalue parameter
+ badvalue - bad pixels defined by maskvalue parameter
+ novalue - pixels with no value defined by maskvalue parameter
+ goodbits - good pixels defined by maskvalue parameter
+ badbits - bad pixels defined by maskvalue parameter
+.fi
+
+Except for "none", these choices use the mask specified by the header
+keyword BPM. To use a different keyword to specify the mask the syntax
+is
+
+.nf
+ !<keyword> [goodvalue|badvalue|novalue|goodbits|badbits]
+.fi
+
+where if the optional second word is missing the default is "goodvalue".
+
+If "none" (or "") no pixel masking is done
+even if an image has an associated pixel mask. The masking defines
+pixels to be used (good) and not used (bad). The types use the
+"maskvalue" parameter to define a single value (either as a number or
+set of bits) for good or bad and all other values are treated as the
+opposite; i.e. bad or good respectively.
+
+The "novalue" choice uses 0 as the good value and all other values are
+bad. However, the "maskvalue" parameter defines a mask value for pixels
+with no value such as occurs from rebinning at the edges or stacking where
+there is no overlap at all. The distinction pixels is that when a final pixel
+has no overlapping data because all input pixels have a "no value" flag
+the blank value is output while if there is no good data then pixels which
+are have other than the "no value" flag are combined as if they were good
+to produce a representative output value. An output mask will have a
+value of 0 for pixels where at least one good input value was present,
+a value of 1 when there was no overlapping data, and a value of 2 when
+bad data was used.
+.le
+.ls maskvalue = 0
+Mask value used with the \fImasktype\fR parameter. If the mask type
+selects good or bad bits the value may be specified using IRAF notation
+for decimal, octal, or hexadecimal; i.e 12, 14b, 0cx to select bits 3
+and 4.
+.le
+.ls blank = 0.
+Output value to be used when there are no pixels for combining after any
+rejection.
+.le
+
+.ls scale = "none" (none|mode|median|mean|exposure|@<file>|!<keyword>)
+Multiplicative image scaling to be applied. The choices are none, multiply
+by the reciprocal of the mode, median, or mean of the specified statistics
+section, multiply by the reciprocal of the exposure time in the image header,
+multiply by the values in a specified file, or multiply by a specified
+image header keyword. When specified in a file the scales must be one per
+line in the order of the input images.
+.le
+.ls zero = "none" (none|mode|median|mean|@<file>|!<keyword>)
+Additive zero level image shifts to be applied. The choices are none, add
+the negative of the mode, median, or mean of the specified statistics
+section, add the values given in a file, or add the values given by an
+image header keyword. When specified in a file the zero values must be one
+per line in the order of the input images. File or keyword zero offset
+values do not allow a correction to the weights.
+.le
+.ls weight = "none" (none|mode|median|mean|exposure|@<file>|!<keyword>)
+Weights to be applied during the final averaging. The choices are none,
+the mode, median, or mean of the specified statistics section, the exposure
+time, values given in a file, or values given by an image header keyword.
+When specified in a file the weights must be one per line in the order of
+the input images and the only adjustment made by the task is for the number of
+images previously combined. In this case the weights should be those
+appropriate for the scaled images which would normally be the inverse
+of the variance in the scaled image.
+.le
+.ls statsec = ""
+Section of images to use in computing image statistics for scaling and
+weighting. If no section is given then the entire region of the input is
+sampled (for efficiency the images are sampled if they are big enough).
+When the images are offset relative to each other one can precede the image
+section with one of the modifiers "input", "output", "overlap". The first
+interprets the section relative to the input image (which is equivalent to
+not specifying a modifier), the second interprets the section relative to
+the output image, and the last selects the common overlap and any following
+section is ignored.
+.le
+.ls expname = ""
+Image header keyword to be used with the exposure scaling and weighting
+options. Also if an exposure keyword is specified that keyword will be
+added to the output image using a weighted average of the input exposure
+values.
+.le
+
+.ce
+Algorithm Parameters
+.ls lthreshold = INDEF, hthreshold = INDEF
+Low and high thresholds to be applied to the input pixels. This is done
+before any scaling, rejection, and combining. If INDEF the thresholds
+are not used.
+.le
+.ls nlow = 1, nhigh = 1 (minmax)
+The number of low and high pixels to be rejected by the "minmax" algorithm.
+These numbers are converted to fractions of the total number of input images
+so that if no rejections have taken place the specified number of pixels
+are rejected while if pixels have been rejected by masking, thresholding,
+or non-overlap, then the fraction of the remaining pixels, truncated
+to an integer, is used.
+.le
+.ls nkeep = 1
+The minimum number of pixels to retain or the maximum number to reject
+when using the clipping algorithms (ccdclip, crreject, sigclip,
+avsigclip, or pclip). When given as a positive value this is the minimum
+number to keep. When given as a negative value the absolute value is
+the maximum number to reject. The latter is in addition to pixels
+missing due to non-overlapping offsets, bad pixel masks, or thresholds.
+.le
+.ls mclip = yes (ccdclip, crreject, sigclip, avsigcliip)
+Use the median as the estimate for the true intensity rather than the
+average with high and low values excluded in the "ccdclip", "crreject",
+"sigclip", and "avsigclip" algorithms? The median is a better estimator
+in the presence of data which one wants to reject than the average.
+However, computing the median is slower than the average.
+.le
+.ls lsigma = 3., hsigma = 3. (ccdclip, crreject, sigclip, avsigclip, pclip)
+Low and high sigma clipping factors for the "ccdclip", "crreject", "sigclip",
+"avsigclip", and "pclip" algorithms. They multiply a "sigma" factor
+produced by the algorithm to select a point below and above the average or
+median value for rejecting pixels. The lower sigma is ignored for the
+"crreject" algorithm.
+.le
+.ls rdnoise = "0.", gain = "1.", snoise = "0." (ccdclip, crreject)
+Readout noise in electrons, gain in electrons/DN, and sensitivity noise as
+a fraction. These parameters are used with the "ccdclip" and "crreject"
+algorithms as well as with the "nmodel" combining option. The values may
+be either numeric or an image header keyword which contains the value.
+The noise model for a pixel is:
+
+.nf
+ variance in DN = (rdnoise/gain)^2 + DN/gain + (snoise*DN)^2
+ variance in e- = (rdnoise)^2 + (gain*DN) + (snoise*(gain*DN))^2
+ = rdnoise^2 + Ne + (snoise * Ne)^2
+.fi
+
+where DN is the data number and Ne is the number of electrons. Sensitivity
+noise typically comes from noise introduced during flat fielding.
+.le
+.ls sigscale = 0.1 (ccdclip, crreject, sigclip, avsigclip)
+This parameter determines when poisson corrections are made to the
+computation of a sigma for images with different scale factors. If all
+relative scales are within this value of unity and all relative zero level
+offsets are within this fraction of the mean then no correction is made.
+The idea is that if the images are all similarly though not identically
+scaled, the extra computations involved in making poisson corrections for
+variations in the sigmas can be skipped. A value of zero will apply the
+corrections except in the case of equal images and a large value can be
+used if the sigmas of pixels in the images are independent of scale and
+zero level.
+.le
+.ls pclip = -0.5 (pclip)
+Percentile clipping algorithm parameter. If greater than
+one in absolute value then it specifies a number of pixels above or
+below the median to use for computing the clipping sigma. If less
+than one in absolute value then it specifies the fraction of the pixels
+above or below the median to use. A positive value selects a point
+above the median and a negative value selects a point below the median.
+The default of -0.5 selects approximately the quartile point.
+See the DESCRIPTION section for further details.
+.le
+.ls grow = 0.
+Radius in pixels for additional pixel to be rejected in an image with a
+rejected pixel from one of the rejection algorithms. This applies only to
+pixels rejected by one of the rejection algorithms and not the masked or
+threshold rejected pixels.
+.le
+
+.ce
+Environment Variables
+
+.ls imcombine_maxmemory (default = 250000000)
+This task tries to use the maximum possible memory for efficiency when
+dealing with lots of data and is designed to reduce memory usage if
+memory allocation fails. However, there may be cases where this adjustment
+fails so this variable allows forcing the task to stay within a smaller
+allocation. This variable is in bytes and the default is the amount
+generally returned by the system. It is large because of virtual memory
+functionality. If problems are encountered one should try setting this
+variable to a smaller size until, hopefully, the out of memory errors
+disappear.
+.le
+.ls imcombine_option (default = 1)
+This environment variable is used to select certain experimental or
+diagnostic options. If this variable has the value 1, the default when the
+variable is undefined, then when the number of images exceeds the number of
+files that can be kept open under IRAF (currently this means more than 4000
+images) the images are closed and opened as needed. This is in contrast to
+the previous method, when the variable has the value 0, which first builds
+a single stacked image of a higher dimension from the input images. This
+method requires the images all have the same size and also that there be
+sufficient disk space for the stacked image and that the image be less
+than 2Gb in size.
+.le
+.ih
+DESCRIPTION
+A set of images or the highest dimension elements (for example
+the planes in an image cube) are combined by weighted averaging, medianing,
+or summing. Pixels may be rejected from the combining by using pixel
+masks, threshold levels, and rejection algorithms. The images may be
+scaled, before rejections, multiplicatively, additively, or both based on
+image statistics, image header keywords, or text files. The images may be
+combined with integer pixel coordinate offsets, possibly determined using
+the world coordinate system of the images, to produce an image bigger than
+any of the input images.
+
+The input images to be combined are specified by a list. If the
+\fBproject\fR parameter is "yes" then the highest dimension elements of
+each input image are combined to make an output image of one lower
+dimension. There is no limit to the number of elements combined in this
+case. If \fBproject\fR is "no" then the entire input list is combined to
+form a single output image. In this case the images must all have the
+same dimensionality but they may have different sizes. There is a software
+limit of approximately 4000 images which may be open
+simultaneously. To combine more than this number the program may either
+create a temporary stacked image, requiring the images to be of the same
+size, or repeatedly open and close the images. See the "Environment
+Variables" in the PARAMETERS section.
+
+The output image header is a copy of the first image in the combined set.
+In addition, the number of images combined is recorded under the keyword
+NCOMBINE. The value of a keyword in the input images, where the
+keyword is specified by the parameter \fIimcmb\fR, is written to an
+indexed keyword IMCMBnnn. The purpose of the ICMBnnn keywords is to
+identify the contributors to the output image. One common choice is
+the input image name though other identifiers may be used.
+
+If a bad pixel mask is created, the name of the mask will be included in the
+output image header under the keyword BPM. The output pixel type is set by
+the parameter \fIouttype\fR. If left blank then the input datatype of
+highest precision is used. If there is a mixture of short and unsigned
+short images then the highest precision is integer.
+
+In addition to one or more output combined images there are some optional
+output files which may be specified as described in the OPTIONAL OUTPUT
+section.
+
+An outline of the steps taken by the program is given below and the
+following sections elaborate on the steps.
+
+.nf
+o Check the input images and stack them if needed
+o Set the input image offsets and the final output image size.
+o Set the input image scales and weights possibly by computing
+ image statistics
+o Write the log file and optional header output
+.fi
+
+For each output image line:
+
+.nf
+o Get input image lines that overlap the output image line
+o Reject masked pixels
+o Reject pixels outside the threshold limits
+o Reject pixels using the specified algorithm
+o Reject neighboring pixels along each line
+o Combine remaining pixels using the weighted average or median
+o Compute sigmas of remaining pixels about the combined values
+o Write the output image line and other optional images.
+.fi
+
+OPTIONAL OUTPUTS
+
+There are various additional outputs that may be produced by providing
+the filenames.
+
+.ls Headers
+The output image can only have one set of header keywords which are
+inherited from the first input image in the input list. Copies of all the
+input headers may be stored in a multiextension FITS file specified by the
+\fIheaders\fR parameter. The extension names are the input image names.
+The extensions are dataless headers. Since this means the image sizes are
+lost, AXLEN keywords are added. Also the keywords INIMAGE and OUTIMAGE are
+added giving the name of the input image and the name of the output
+combined image.
+.le
+.ls Bad Pixel Masks
+The \fIbpmasks\fR parameter produces optional output bad pixel mask(s) with
+good values of 0 and bad values of 1. Output pixels are marked as bad when
+no input pixels contributed to the output pixel. The file name is also
+added to the output image header under the keyword BPM.
+.le
+.ls Rejection Masks
+The \fIrejmasks\fR parameter produces optional output mask file(s)
+identifying rejected or excluded pixels. The pixel mask is the size of the
+output image. There is one extra dimension with length equal to the number
+of input images. Each element of the highest dimension is a mask
+corresponding to an input image with values of 1 for rejected or excluded
+pixels and values of 0 for pixels which were used. The order of the masks
+is the order of the input images and image header keywords indexed by the
+element identify the input images. Note that the pixel positions are in
+the output pixel coordinate system.
+
+This mask is the only way to record whether a particular input image pixel
+contributed to the output image. As an example, consider the case of
+three input two dimensional images of sizes 1020x1020, 1010x1010, and
+1000x1000 with relative offsets of (0,0), (10,10), and (20,20). The output
+image would then be 1020x1020.
+
+Suppose that the only input pixels not used are pixels (1,1) in each input
+image. Because of the offsets the first 10 rows and columns of the output
+will be based on just a single pixel except for (1,1) which has no input
+pixels. The next 10 rows and columns of the output will be a combination
+of 2 input pixels except (11,11) which is just based on pixel (11,11)
+in the first input image. Finally all other pixels except (21,21) will be
+a combination of 3 values.
+
+The rejection mask will be three dimensional of size 1020x1020x3. Plane 1
+will correspond to the first input image, plane 2 to the second, and so
+on. All of the pixels will be zero except for the following pixels
+which will have a value of 1. In the first plane only pixel (1,1,1) will be
+one. In the second plane the first 10 rows and columns and pixel (11,11,2)
+will be one. And in the third plane, the first 20 rows and columns and pixel
+(21,21,3) will be one. So if we want to know about output pixel (11,11)
+the rejection mask will tell us that pixels from the second and third
+images were excluded.
+
+This is a complex example because of the offsets and dissimilar sizes.
+In the more common and simpler case of equal sizes and registered images,
+the mask
+planes would have one to indicate that the pixel in the input image at
+that coordinate was not used. For instance if pixel (12,15,2) is one
+in the rejection mask then pixel (12,15) in the second input image was
+excluded.
+
+Note that one can use image sections to extract a mask matching the input
+image. For the example case with the offsets masks in the input
+coordinates can be extracted with the commands
+
+.nf
+ cl> imcopy rejmask[*,*,1] mask1
+ cl> imcopy rejmask[11:1020,11:1020,2] mask2
+ cl> imcopy rejmask[21:1020,21:1020,3] mask3
+.fi
+
+For the case of equal sized and registered images one could also use
+\fBimslice\fR.
+.le
+.ls Mask of the Number of Rejected Pixels
+The \fInrejmasks\fR parameter produces optional pixel mask(s) giving the
+number of input pixels rejected or excluded from the input images. This is
+equivalent to projecting the rejection mask described previously by summing
+along the highest dimension. Note that in this mask a value of 0 indicates
+all the input pixels were used to create the output pixel and a value equal
+to the number of input images indicate no input pixels were used.
+.le
+.ls Exposure Masks
+The \fIexpmasks\fR parameter produces optional output exposure mask(s)
+giving the sum of the exposure values of the input images with non-zero
+weights that contributed to that pixel. Since masks are integer, the
+exposure values may be scaled to preserve dynamic range and fractional
+significance. The scaling values are given in the header under the
+keywords MASKSCAL and MASKZERO. Exposure values are computed from the mask
+values by scale * value + zero where scale is the value of the MASKSCAL
+keyword and zero is the value of the MASKZERO keyword.
+.le
+.ls Sigma of Combined Pixels
+The \fIsigma\fR parameter produces optional output sigma image(s). The
+sigma is the standard deviation, corrected for a finite population, of the
+input pixel values (excluding rejected pixels) about the output combined
+pixel values.
+.le
+.ls Output Log File
+The \fIlogfile\fR parameter produces an optional output log file. If no
+file is specified then no log information is produced. The special
+filename "STDOUT" prints log information to the terminal.
+.le
+
+OFFSETS
+
+The images to be combined need not be of the same size or overlap. They
+do have to have the same dimensionality which will also be the dimensionality
+of the output image. Any dimensional images supported by IRAF may be
+used. Note that if the \fIproject\fR flag is "yes" then the input images
+are the elements of the highest dimension; for example the planes of a
+three dimensional image.
+
+The overlap of the images is determined by a set of integer pixel offsets
+with an offset for each dimension of each input image. For example
+offsets of 0, 10, and 20 in the first dimension of three images will
+result in combining the three images with only the first image in the
+first 10 columns, the first two images in the next 10 columns and
+all three images starting in the 21st column. At the 21st output column
+the 21st column of the first image will be combined with the 11th column
+of the second image and the 1st column of the third image.
+
+The output image size is set by the maximum extent in each dimension
+of any input image after applying the offsets. In the above example if
+all the images have 100 columns then the output image will have 120
+columns corresponding to the 20 column offset in the third image.
+Note that this same output image size is computed and used as the
+basis for the \fIoutlimits\fR parameter to specify a subregion to
+actually be output.
+
+The input image offsets are set using the \fIoffset\fR parameter. There
+are four ways to specify the offsets. If the word "none" or the empty
+string "" are used then all offsets will be zero and all pixels with the
+same coordinates will be combined. The output image size will be equal to
+the biggest dimensions of the input images.
+
+If "wcs" offsets are specified then the world coordinate systems (wcs)
+in the image headers are used to derived the offsets. The world coordinate
+at the center of the first input image is evaluated. Then integer pixel
+offsets are determined for each image to bring the same world coordinate
+to the same point. Note the following caveats. The world coordinate
+systems must be of the same type, orientation, and scale and only the
+nearest integer shift is used.
+
+If the input images have offsets in a regular grid or one wants to make
+an output image in which the input images are "mosaiced" together in
+a grid then the special offset string beginning with the word "grid"
+is used. The format is
+
+.nf
+ grid [n1] [s1] [n2] [s2] ...
+.fi
+
+where ni is the number of images in dimension i and si is the step in
+dimension i. For example "grid 5 100 5 100" specifies a 5x5 grid with
+origins offset by 100 pixels. Note that one must insure that the input
+images are specified in the correct order. This may best be accomplished
+using a "@" list. One useful application of the grid is to make a
+non-overlapping mosaic of a number of images for display purposes. Suppose
+there are 16 images which are 100x100. The offset string "grid 4 101 4
+101" will produce a mosaic with a one pixel border having the value set
+by \fIblank\fR parameter between the images.
+
+The offsets may be defined in a file by specifying the file name
+in the \fIoffset\fR parameter. (Note that the special file name STDIN
+may be used to type in the values terminated by the end-of-file
+character). The file consists of a line for each input image. The lines
+must be in the same order as the input images and so an "@" list may
+be useful. The lines consist of whitespace separated offsets one for
+each dimension of the images. In the first example cited above the
+offset file might contain:
+
+.nf
+ 0 0
+ 10 0
+ 20 0
+.fi
+
+where we assume the second dimension has zero offsets.
+
+The offsets need not have zero for one of the images. The offsets may
+include negative values or refer to some arbitrary common point.
+When the offsets are read by the program it will find the minimum
+value in each dimension and subtract it from all the other offsets
+in that dimension. The above example could also be specified as:
+
+.nf
+ 225 15
+ 235 15
+ 245 15
+.fi
+
+There may be cases where one doesn't want the minimum offsets reset
+to zero. If all the offsets are positive and the comment "# Absolute"
+appears in the offset file then the images will be combined with
+blank values between the first output pixel and the first overlapping
+input pixel. Continuing with the above example, the file
+
+.nf
+ # Absolute
+ 10 10
+ 20 10
+ 30 10
+.fi
+
+will have the first pixel of the first image in the 11th pixel of the
+output image. Note that there is no way to "pad" the other side of
+the output image.
+
+OUTPUT OF SUBREGIONS
+
+The output image size is computed from all of the input images including
+the offsets as described previously. The \fIoutlimits\fR may be used to
+specify a subregion of this full size to be created. The syntax of this
+parameter is pairs of whitespace separated numbers selecting the first and last
+pixel in each output dimension. The pairs for each dimension are also
+whitespace separated and are given in order of the dimensions. Any missing
+values at the end of the string default to the full limits of the output
+image. If the requested limits fall outside the full output image they are
+reset to the size of the full computed output size.
+
+As an example, consider combining 10 images of size 1000x1000 with offsets
+of 0, 1, ..., 9 along the first dimension. Because of the offsets the full
+output size is 1010x1000. To output only the region [1:100,101:200]
+of this full size the parameter value would be the string "1 100 101 200".
+Note that if the value was just "1 100" then the output region would
+be [1:100,1:1000].
+
+The intended purpose for this option is to allow creating subregions using
+a smaller number of images in the case of offset data taken at a raster of
+positions. This is important since when the number of images becomes too
+large (>4000) the program either has to prestack the images into a higher
+dimensional single image (requiring equal sized images) or utilize an
+inefficient algorithm where images are opened and closed for each input
+line. A detail of how this task works is that it is the number of images
+required to be accessed for each output line that is significant.
+
+The following example was developed when the maximum number of images
+open at one time was ~240. In V2.12 the number was increased to
+more than 4000 so it is not as relevant though it may apply to very
+large surveys with many small images.
+
+As an example, consider a survey of a region of the sky composed of 8000
+images which are each 500x1000. The offsets between each image are 50
+pixels along the first dimension and 900 pixels along the second dimension,
+give or take a few pixels due to telescope pointing errors. Thus this
+survey consists of strips of exposures. Within a strip the images over by
+about 450 pixels. Between strips the overlap is 100 pixels. So the
+strips consist 400 exposures and there are 20 strips.
+
+The full size of this survey is then about 20450x18900. At any point in a
+single strip the number of images contributing is no more than 10.
+Including the overlap of the strips the maximum number is then 20. In
+order to combine the data for such a survey one would like to create
+subregion outputs which are 120 images from each strip. The lines where
+the two strips overlap then require 240 images. To produce roughly equal
+size regions we choose sizes along the first dimension of 5200 pixels. The
+number of lines in the output subregions might be the full size of the
+survey. However, it might be desirable to also break the second dimension
+into blocks for ease of display and manipulation.
+
+The method for combining this example survey is then to combine the data in
+four groups along the first dimension to produce subimages each 5200 pixels
+wide which have no overlap. The reason for wanting to create
+non-overlapping subregions is to simplify creation of the related masks,
+most importantly, the exposure masks. The \fIoutlimits\fR parameter would
+have the values "1 5200", "5201 10400", "10401 15600", and "15601 20800".
+The second pair of limits is not specified to obtain the full size along
+the second dimension. Note that the last block will actually be smaller
+than 5200 pixels since the survey is less than 20800 pixels.
+
+In each combining step all the images must be specified for the input in
+order to compute the full output size but then only those images needed to
+produce an output line will be accessed at the same time. By design this
+is roughly 240 images for lines where the strips overlap. The
+non-overlapping blocks can be mosaiced together with this task as a final
+step if desired.
+
+
+SCALES AND WEIGHTS
+
+In order to combine images with rejection of pixels based on deviations
+from some average or median they must be scaled to a common level. There
+are two types of scaling available, a multiplicative intensity scale and an
+additive zero point shift. The intensity scaling is defined by the
+\fIscale\fR parameter and the zero point shift by the \fIzero\fR
+parameter. These parameters may take the values "none" for no scaling,
+"mode", "median", or "mean" to scale by statistics of the image pixels,
+"exposure" (for intensity scaling only) to scale by the exposure time
+keyword in the image header, any other image header keyword specified by
+the keyword name prefixed by the character '!', and the name of a file
+containing the scale factors for the input image prefixed by the
+character '@'.
+
+Examples of the possible parameter values are shown below where
+"myval" is the name of an image header keyword and "scales.dat" is
+a text file containing a list of scale factors.
+
+.nf
+ scale = none No scaling
+ zero = mean Intensity offset by the mean
+ scale = exposure Scale by the exposure time
+ zero = !myval Intensity offset by an image keyword
+ scale = @scales.dat Scales specified in a file
+.fi
+
+The image statistics are computed by sampling a uniform grid of points with
+the smallest grid step that yields less than 100000 pixels; sampling is used
+to reduce the time needed to compute the statistics. If one wants to
+restrict the sampling to a region of the image the \fIstatsec\fR parameter
+is used. This parameter has the following syntax:
+
+.nf
+ [input|output|overlap] [image section]
+.fi
+
+The initial modifier defaults to "input" if absent. The modifiers are useful
+if the input images have offsets. In that case "input" specifies
+that the image section refers to each input image, "output" specifies
+that the image section refers to the output image coordinates, and
+"overlap" specifies the mutually overlapping region of the input images.
+In the latter case an image section is ignored.
+
+The statistics are as indicated by their names. In particular, the
+mode is a true mode using a bin size which is a fraction of the
+range of the pixels and is not based on a relationship between the
+mode, median, and mean. Also masked pixels are excluded from the
+computations as well as during the rejection and combining operations.
+
+The "exposure" option in the intensity scaling uses the value of the
+image header keyword specified by the \fIexpname\fR keyword. As implied
+by the parameter name, this is typically the image exposure time since
+intensity levels are linear with the exposure time in CCD detectors.
+Note that the exposure keyword is also updated in the final image
+as the weighted average of the input values. Thus, if one wants to
+use a nonexposure time keyword and keep the exposure time updating
+feature the image header keyword syntax is available; i.e. !<keyword>.
+
+Scaling values may be defined as a list of values in a text file. The file
+name is specified by the standard @file syntax. The list consists of one
+value per line. The order of the list is assumed to be the same as the
+order of the input images. It is a fatal error if the list is incomplete
+and a warning if the list appears longer than the number of input images.
+Because the scale and zero levels are adjusted only the relative
+values are important.
+
+If both an intensity scaling and zero point shift are selected the
+zero point is added first and the scaling is done. This is
+important if the scale and offset values are specified by
+header keywords or from a file of values. However,
+in the log output the zero values are given as the scale times
+the offset hence those numbers would be interpreted as scaling
+first and zero offset second.
+
+The image statistics and scale factors are recorded in the log file
+unless they are all equal, which is equivalent to no scaling. The
+scale factors are normalized so that the first input image has no
+scaling. This is done because the header of the first input image
+is used as the template header for the combined output image.
+By scaling to this first image this means that flux related keywords,
+such as exposure time and airmass, are representative of the output
+(except when the "sum" option is used).
+
+Scaling affects not only the mean values between images but also the
+relative pixel uncertainties. For example scaling an image by a
+factor of 0.5 will reduce the effective noise sigma of the image
+at each pixel by the square root of 0.5. Changes in the zero
+point also changes the noise sigma if the image noise characteristics
+are Poissonian. In the various rejection algorithms based on
+identifying a noise sigma and clipping large deviations relative to
+the scaled median or mean, one may need to account for the scaling induced
+changes in the image noise characteristics.
+
+In those algorithms it is possible to eliminate the "sigma correction"
+while still using scaling. The reasons this might be desirable are 1) if
+the scalings are similar the corrections in computing the mean or median
+are important but the sigma corrections may not be important and 2) the
+image statistics may not be Poissonian, either inherently or because the
+images have been processed in some way that changes the statistics. In the
+first case because computing square roots and making corrections to every
+pixel during the iterative rejection operation may be a significant
+computational speed limit the parameter \fIsigscale\fR selects how
+dissimilar the scalings must be to require the sigma corrections. This
+parameter is a fractional deviation which, since the scale factors are
+normalized to unity, is the actual minimum deviation in the scale factors.
+For the zero point shifts the shifts are normalized by the mean shift
+before adjusting the shifts to a zero mean. To always use sigma scaling
+corrections the parameter is set to zero and to eliminate the correction in
+all cases it is set to a very large number.
+
+If the final combining operation is "average" then the images may be
+weighted during the averaging. The weights are specified in the same way
+as the scale factors. In addition the NCOMBINE keyword, if present, will
+be used in the weights. The weights, scaled to a unit sum, are printed in
+the log output.
+
+The weights are used for the final weighted average, sigma image, and
+exposure mask output. They are not used to form averages in the various
+rejection algorithms. For weights in the case of no scaling or only
+multiplicative scaling the weights are used as given or determined so that
+images with lower signal levels will have lower weights. However, for
+cases in which zero level scaling is used and the zero levels are
+determined from image statistics (not from an input file or keyword) the
+weights are computed from the initial weights (the exposure time, image
+statistics, or input values) using the formula:
+
+.nf
+ weight_final = weight_initial / (scale * sky)
+.fi
+
+where the sky values are those from the image statistics before conversion
+to zero level shifts and adjustment to zero mean over all images. The
+reasoning is that if the zero level is high the sky brightness is high and
+so the S/N is lower and the weight should be lower. If any sky value
+determined from the image statistics comes out to be negative a warning is
+given and the none of the weight are adjusted for sky levels.
+
+The weights are not adjusted when the zero offsets are input from a file
+or keyword since these values do not imply the actual image sky value.
+In this case if one wants to account for different sky statistics
+in the weights the user must specify the weights in a file taking
+explicit account of changes in the weights due to different sky
+statistics.
+
+When forming the final weighted averages if the sum of the weights of
+the non-rejected or excluded pixels is zero then instead of producing
+a zero average the unweighted average of the pixels is produced. Similarly,
+in the sigma calculation when the weights of the pixels are all zero
+then the sigma is computed as if all pixels have unit weights.
+
+When there are zero weights only the pixels with non-zero weights are
+used in computing the output exposure time mask. Note that the actual
+weight values are not used but simply the sum of all exposure times
+of pixels from images with non-zero weights is produced.
+
+The purpose of using zero weights is to identify images that are of
+poor quality (such as non-photometric or bad seeing) which are then
+excluded in the final weighted average or exposure time. However,
+they contribute to the final image when there is no good
+quality data but with an output exposure time of zero.
+
+INPUT PIXEL MASKS
+
+A pixel mask is a type of IRAF file having the extension ".pl" or
+a FITS extension of "type=mask" which
+identifies an integer value with each pixel of the images to which it is
+applied. In future masks may also be stored as special FITS extensions.
+The integer values may denote regions, a weight, a good or bad
+flag, or some other type of integer or integer bit flag. In the common
+case where many values are the same, this type of file is compact.
+It is most compact and efficient if the majority of
+the pixels have a zero mask value so frequently zero is the value for good
+pixels. Note that these files, while not stored as a strict pixel array,
+may be treated as images in programs. This means they may be created by
+programs such as \fBmkpattern\fR, edited by \fBimedit\fR, examined by
+\fBimexamine\fR, operated upon by \fBimarith\fR, graphed by \fBimplot\fR,
+and displayed by \fBdisplay\fR.
+
+To use pixel masks with \fBimcombine\fR one must associate a pixel
+mask file with an image by entering the pixel list file name in the
+image header under the keyword BPM (bad pixel mask) or some other
+keyword to be specified. This can be
+done with \fBhedit\fR. Note that the same pixel mask may be associated
+with more than one image as might be the case if the mask represents
+defects in the detector used to obtain the images.
+
+If a pixel mask is associated with an image the mask is used when the
+\fImasktype\fR parameter is set to a value other than "none" or "". Note that
+when it is set to "none", mask information is not used even if it exists for
+the image. The values of \fImasktype\fR which apply masks are "goodvalue",
+"badvalue", "novalue", "goodbits", "badbits", and "!<keyword>". The last choice
+allows specifying the keyword whose value is the mask to be used otherwise
+the keyword "BPM" is used.
+
+The \fImasktype\fR choices are used in conjunction with the
+\fImaskvalue\fR parameter. When the mask type is "goodvalue" or an
+explicit keyword is specified without a mask type, the pixels with mask
+values matching the specified value are included in combining and all
+others are rejected. For a mask type of "badvalue" the pixels with
+mask values matching the specified value are rejected and all others
+are accepted. The bit types are useful for selecting a combination of
+attributes in a mask consisting of bit flags. The mask value is still
+an integer but is interpreted by bitwise comparison with the values in
+the mask file.
+
+The "novalue" option differs from the others in that there are three
+classes of mask values and any output pixel mask will have the three
+values 0 for good, 1 for no data, and 2 for bad. The purpose of this
+option is to produce output values from the input values when there are
+no good pixels. This happens when the input images have pixel values
+which have been identified as bad (such as saturated) but whose values
+can be used, possibly after being replaced or interpolated from nearby
+pixels, to produce a value that is either cosmetically reasonable or even
+marginally scientifically useful. Again, this only happens if there
+are no good pixels to combine and then the output mask will identify
+these pixels with a mask value of 2. If there is even one good pixel
+then only the good data will contribute to the output. An exposure mask
+may be useful in this case when most but not all image pixels have been
+eliminated due to things like saturation.
+
+If a mask operation is specified and an image has no mask image associated
+with it (the BPM or specified keyword is absent), the mask values are taken
+as all zeros. In those cases be careful that zero is an accepted value
+otherwise the entire image will be rejected.
+
+When the number of input images exceeds the maximum number of open files
+allowed by IRAF and the input images need to be "stacked" then the masks
+are also stacked. The stacking requires all the images to have the same size.
+
+
+THRESHOLD REJECTION
+
+In addition to rejecting masked pixels, pixels in the unscaled input
+images which are below or above the thresholds given by the parameters
+\fIlthreshold\fR and \fIhthreshold\fR are rejected. Values of INDEF
+mean that no threshold value is applied. Threshold rejection may be used
+to exclude very bad pixel values or as an alternative way of masking
+images. In the latter case one can use a task like \fBimedit\fR
+or \fBimreplace\fR to set parts of the images to be excluded to some
+very low or high magic value.
+
+
+REJECTION ALGORITHMS
+
+The \fIreject\fR parameter selects a type of rejection operation to
+be applied to pixels not masked or thresholded. If no rejection
+operation is desired the value "none" is specified.
+
+.in 2
+MINMAX
+.in 2
+A specified fraction of the highest and lowest pixels are rejected.
+The fraction is specified as the number of high and low pixels, the
+\fInhigh\fR and \fInlow\fR parameters, when data from all the input images
+are used. If pixels have been rejected by offseting, masking, or
+thresholding then a matching fraction of the remaining pixels, truncated
+to an integer, are used. Thus,
+
+.nf
+ nl = n * nlow/nimages + 0.001
+ nh = n * nhigh/nimages + 0.001
+.fi
+
+where n is the number of pixels surviving offseting, masking, and
+thresholding, nimages is the number of input images, nlow and nhigh
+are task parameters and nl and nh are the final number of low and
+high pixels rejected by the algorithm. The factor of 0.001 is to
+adjust for rounding of the ratio.
+
+As an example with 10 input images and specifying one low and two high
+pixels to be rejected the fractions to be rejected are nlow=0.1 and nhigh=0.2
+and the number rejected as a function of n is:
+
+.nf
+ n 0 1 2 3 4 5 6 7 8 9 10
+ nl 0 0 0 0 0 0 0 0 0 0 1
+ nh 0 0 0 0 0 1 1 1 1 1 2
+.fi
+
+.in -2
+CCDCLIP
+.in 2
+If the images are obtained using a CCD with known read out noise, gain, and
+sensitivity noise parameters and they have been processed to preserve the
+relation between data values and photons or electrons then the noise
+characteristics of the images are well defined. In this model the sigma in
+data values at a pixel with true value <I>, as approximated by the median
+or average with the lowest and highest value excluded, is given by:
+
+.nf
+ sigma = ((rn / g) ** 2 + <I> / g + (s * <I>) ** 2) ** 1/2
+.fi
+
+where rn is the read out noise in electrons, g is the gain in
+electrons per data value, s is a sensitivity noise given as a fraction,
+and ** is the exponentiation operator. Often the sensitivity noise,
+due to uncertainties in the pixel sensitivities (for example from the
+flat field), is not known in which case a value of zero can be used.
+See the task \fBstsdas.wfpc.noisemodel\fR for a way to determine
+these values (though that task expresses the read out noise in data
+numbers and the sensitivity noise parameter as a percentage).
+
+The read out noise is specified by the \fIrdnoise\fR parameter. The value
+may be a numeric value to be applied to all the input images or a image
+header keyword containing the value for each image. Similarly, the
+parameter \fIgain\fR specifies the gain as either a value or image header
+keyword and the parameter \fIsnoise\fR specifies the sensitivity
+noise parameter as either a value or image header keyword.
+
+The algorithm operates on each output pixel independently. It starts by
+taking the median or unweighted average (excluding the minimum and maximum)
+of the unrejected pixels provided there are at least two input pixels. The
+expected sigma is computed from the CCD noise parameters and pixels more
+that \fIlsigma\fR times this sigma below or \fIhsigma\fR times this sigma
+above the median or average are rejected. The process is then iterated
+until no further pixels are rejected. If the average is used as the
+estimator of the true value then after the first round of rejections the
+highest and lowest values are no longer excluded. Note that it is possible
+to reject all pixels if the average is used and is sufficiently skewed by
+bad pixels such as cosmic rays.
+
+If there are different CCD noise parameters for the input images
+(as might occur using the image header keyword specification) then
+the sigmas are computed for each pixel from each image using the
+same estimated true value.
+
+If the images are scaled and shifted and the \fIsigscale\fR threshold
+is exceedd then a sigma is computed for each pixel based on the
+image scale parameters; i.e. the median or average is scaled to that of the
+original image before computing the sigma and residuals.
+
+After rejection the number of retained pixels is checked against the
+\fInkeep\fR parameter. If there are fewer pixels retained than specified
+by this parameter the pixels with the smallest residuals in absolute
+value are added back. If there is more than one pixel with the same
+absolute residual (for example the two pixels about an average
+or median of two will have the same residuals) they are all added
+back even if this means more than \fInkeep\fR pixels are retained.
+Note that the \fInkeep\fR parameter only applies to the pixels used
+by the clipping rejection algorithm and does not apply to threshold
+or bad pixel mask rejection.
+
+This is the best clipping algorithm to use if the CCD noise parameters are
+adequately known. The parameters affecting this algorithm are \fIreject\fR
+to select this algorithm, \fImclip\fR to select the median or average for
+the center of the clipping, \fInkeep\fR to limit the number of pixels
+rejected, the CCD noise parameters \fIrdnoise, gain\fR and \fIsnoise\fR,
+\fIlsigma\fR and \fIhsigma\fR to select the clipping thresholds,
+and \fIsigscale\fR to set the threshold for making corrections to the sigma
+calculation for different image scale factors.
+
+.in -2
+CRREJECT
+.in 2
+This algorithm is identical to "ccdclip" except that only pixels above
+the average are rejected based on the \fIhsigma\fR parameter. This
+is appropriate for rejecting cosmic ray events and works even with
+two images.
+
+.in -2
+SIGCLIP
+.in 2
+The sigma clipping algorithm computes at each output pixel the median or
+average excluding the high and low values. The sigma is then computed
+about this estimate (without excluding the low and high values). There
+must be at least three input pixels, though for this method to work well
+there should be at least 10 pixels. Values deviating by more than the
+specified sigma threshold factors are rejected. These steps are repeated,
+except that after the first time the average includes all values, until no
+further pixels are rejected or there are fewer than three pixels.
+
+After rejection the number of retained pixels is checked against the
+\fInkeep\fR parameter. If there are fewer pixels retained than specified
+by this parameter the pixels with the smallest residuals in absolute
+value are added back. If there is more than one pixel with the same
+absolute residual (for example the two pixels about an average
+or median of two will have the same residuals) they are all added
+back even if this means more than \fInkeep\fR pixels are retained.
+Note that the \fInkeep\fR parameter only applies to the pixels used
+by the clipping rejection algorithm and does not apply to threshold
+or bad pixel mask rejection.
+
+The parameters affecting this algorithm are \fIreject\fR to select
+this algorithm, \fImclip\fR to select the median or average for the
+center of the clipping, \fInkeep\fR to limit the number of pixels
+rejected, \fIlsigma\fR and \fIhsigma\fR to select the
+clipping thresholds, and \fIsigscale\fR to set the threshold for
+making corrections to the sigma calculation for different image scale
+factors.
+
+.in -2
+AVSIGCLIP
+.in 2
+The averaged sigma clipping algorithm assumes that the sigma about the
+median or mean (average excluding the low and high values) is proportional
+to the square root of the median or mean at each point. This is
+described by the equation:
+
+.nf
+ sigma(column,line) = sqrt (gain(line) * signal(column,line))
+.fi
+
+where the \fIestimated\fR signal is the mean or median (hopefully excluding
+any bad pixels) and the gain is the \fIestimated\fR proportionality
+constant having units of photons/data number.
+
+This noise model is valid for images whose values are proportional to the
+number of photons recorded. In effect this algorithm estimates a
+detector gain for each line with no read out noise component when
+information about the detector noise parameters are not known or
+available. The gain proportionality factor is computed
+independently for each output line by averaging the square of the residuals
+(at points having three or more input values) scaled by the median or
+mean. In theory the proportionality should be the same for all rows but
+because of the estimating process will vary somewhat.
+
+Once the proportionality factor is determined, deviant pixels exceeding the
+specified thresholds are rejected at each point by estimating the sigma
+from the median or mean. If any values are rejected the median or mean
+(this time not excluding the extreme values) is recomputed and further
+values rejected. This is repeated until there are no further pixels
+rejected or the number of remaining input values falls below three. Note
+that the proportionality factor is not recomputed after rejections.
+
+If the images are scaled differently and the sigma scaling correction
+threshold is exceedd then a correction is made in the sigma
+calculations for these differences, again under the assumption that
+the noise in an image scales as the square root of the mean intensity.
+
+After rejection the number of retained pixels is checked against the
+\fInkeep\fR parameter. If there are fewer pixels retained than specified
+by this parameter the pixels with the smallest residuals in absolute
+value are added back. If there is more than one pixel with the same
+absolute residual (for example the two pixels about an average
+or median of two will have the same residuals) they are all added
+back even if this means more than \fInkeep\fR pixels are retained.
+Note that the \fInkeep\fR parameter only applies to the pixels used
+by the clipping rejection algorithm and does not apply to threshold
+or bad pixel mask rejection.
+
+This algorithm works well for even a few input images. It works better if
+the median is used though this is slower than using the average. Note that
+if the images have a known read out noise and gain (the proportionality
+factor above) then the "ccdclip" algorithm is superior. The two algorithms
+are related in that the average sigma proportionality factor is an estimate
+of the gain.
+
+The parameters affecting this algorithm are \fIreject\fR to select
+this algorithm, \fImclip\fR to select the median or average for the
+center of the clipping, \fInkeep\fR to limit the number of pixels
+rejected, \fIlsigma\fR and \fIhsigma\fR to select the
+clipping thresholds, and \fIsigscale\fR to set the threshold for
+making corrections to the sigma calculation for different image scale
+factors.
+
+.in -2
+PCLIP
+.in 2
+The percentile clipping algorithm is similar to sigma clipping using the
+median as the center of the distribution except that, instead of computing
+the sigma of the pixels from the CCD noise parameters or from the data
+values, the width of the distribution is characterized by the difference
+between the median value and a specified "percentile" pixel value. This
+width is then multiplied by the scale factors \fIlsigma\fR and \fIhsigma\fR
+to define the clipping thresholds above and below the median. The clipping
+is not iterated.
+
+The pixel values at each output point are ordered in magnitude and the
+median is determined. In the case of an even number of pixels the average
+of the two middle values is used as the median value and the lower or upper
+of the two is the median pixel when counting from the median pixel to
+selecting the percentile pixel. The parameter \fIpclip\fR selects the
+percentile pixel as the number (if the absolute value is greater
+than unity) or fraction of the pixels from the median in the ordered set.
+The direction of the percentile pixel from the median is set by the sign of
+the \fIpclip\fR parameter with a negative value signifying pixels with
+values less than the median. Fractional values are internally converted to
+the appropriate number of pixels for the number of input images. A minimum
+of one pixel and a maximum corresponding to the extreme pixels from the
+median are enforced. The value used is reported in the log output. Note
+that the same percentile pixel is used even if pixels have been rejected by
+offseting, masking, or thresholding; for example, if the 3nd pixel below
+the median is specified then the 3rd pixel will be used whether there are
+10 pixels or 5 pixels remaining after the preliminary steps.
+
+After rejection the number of retained pixels is checked against the
+\fInkeep\fR parameter. If there are fewer pixels retained than specified
+by this parameter the pixels with the smallest residuals in absolute
+value are added back. If there is more than one pixel with the same
+absolute residual (for example the two pixels about an average
+or median of two will have the same residuals) they are all added
+back even if this means more than \fInkeep\fR pixels are retained.
+Note that the \fInkeep\fR parameter only applies to the pixels used
+by the clipping rejection algorithm and does not apply to threshold
+or bad pixel mask rejection.
+
+Some examples help clarify the definition of the percentile pixel. In the
+examples assume 10 pixels. The median is then the average of the
+5th and 6th pixels. A \fIpclip\fR value of 2 selects the 2nd pixel
+above the median (6th) pixel which is the 8th pixel. A \fIpclip\fR
+value of -0.5 selects the point halfway between the median and the
+lowest pixel. In this case there are 4 pixels below the median,
+half of that is 2 pixels which makes the percentile pixel the 3rd pixel.
+
+The percentile clipping algorithm is most useful for clipping small
+excursions, such as the wings of bright objects when combining
+disregistered observations for a sky flat field, that are missed when using
+the pixel values to compute a sigma. It is not as powerful, however, as
+using the CCD noise parameters (provided they are accurately known) to clip
+about the median.
+
+The parameters affecting this algorithm are \fIreject\fR to select this
+algorithm, \fIpclip\fR to select the percentile pixel, \fInkeep\fR to limit
+the number of pixels rejected, and \fIlsigma\fR and \fIhsigma\fR to select
+the clipping thresholds.
+.in -4
+
+GROW REJECTION
+
+Neighbors of pixels rejected by the rejection algorithms
+may also be rejected. The number of neighbors to be rejected
+is specified by the \fIgrow\fR parameter which is a radius in pixels.
+If too many pixels are rejected in one of the grown pixels positions
+(as defined by the \fInkeep\fR parameter) then the value of that pixel
+without growing will be used.
+
+COMBINING
+
+After all the steps of offsetting the input images, masking pixels,
+threshold rejection, scaling, and applying a rejection algorithms the
+remaining pixels are combined and output as specified by the \fIcombine\fR
+parameter. In all cases if there are no remaining pixels the \fIblank\fR
+is produced. The combining choices are as follows.
+
+.in 2
+AVERAGE
+.in 2
+The weighted average of the remaining pixels is computed. If no
+weighting was specified then a simple, unweighted average is used.
+If the sum of the weights of for the accepted pixels is zero then the
+unweighted average is output.
+
+.in -2
+MEDIAN
+.in 2
+The median of the remaining pixels is computed. The median is the
+usual mathematical definition where a particular pixel value is produced
+for an odd number of pixels and the average of the two central values
+is computed for an even number of pixels.
+
+.in -2
+SUM
+.in 2
+The sum of the unrejected pixels is computed.
+
+.in -2
+LMEDIAN
+.in 2
+The median of the remaining pixels is computed except that for two
+pixels the lower value is used. This is a specialized feature useful
+for minimizing the effects of cosmic rays in dithered and/or masked data.
+
+.in -2
+QUADRATURE
+.in 2
+The pixels are combined as
+
+.nf
+ sqrt (sum {(wt * sigma)^2}) / sum {wt}
+.fi
+
+This is used when the input pixel values represent "sigmas". This option
+is usually a second pass after the input data has been combined. It is
+important that the input is arranged such that the same scaling and
+pixel rejections are used. This means that these cannot be given by
+explicit lists and masks and not generated from the data.
+
+.in -2
+QUADRATURE
+.in 2
+The pixels are combined as
+
+.nf
+ value = max (0, scaled_pixel_value)
+ variance = rdnoise^2 + value / gain + (snoise * value)^2
+ output = sqrt (sum {variance * wt^2}) / sum {wt}
+.fi
+
+This is used when the variances in the input images can be computed
+by the above noise model. Note that the gain and rdnoise are adjusted
+for any scaling applied to the pixel values.
+
+This method has the advantage that the input images are the same as
+those used to form a combined image and so all the steps of deriving
+scaling and rejecting pixels by some rejection method will be the same.
+.in -4
+
+SIGMA OUTPUT
+
+In addition to the combined image and optional sigma image may be
+produced. The sigma computed is the standard deviation, corrected for a
+finite population by a factor of n/(n-1), of the unrejected input pixel
+values about the output combined pixel values.
+.ih
+EXAMPLES
+1. To average and median images without any other features:
+
+.nf
+ cl> imcombine obj* avg combine=average reject=none
+ cl> imcombine obj* med combine=median reject=none
+.fi
+
+2. To reject cosmic rays:
+
+.nf
+ cl> imcombine obs1,obs2 Obs reject=crreject rdnoise=5.1, gain=4.3
+.fi
+
+3. To make a grid for display purposes with 21 64x64 images:
+
+.nf
+ cl> imcombine @list grid offset="grid 5 65 5 65"
+.fi
+
+4. To apply a mask image with good pixels marked with a zero value and
+bad pixels marked with a value of one:
+
+.nf
+ cl> hedit ims* bpm badpix.pl add+ ver-
+ cl> imcombine ims* final combine=median masktype=goodval
+.fi
+
+5. To scale image by the exposure time and then adjust for varying
+sky brightness and make a weighted average:
+
+.nf
+ cl> imcombine obj* avsig combine=average reject=avsig \
+ >>> scale=exp zero=mode weight=exp expname=exptime
+.fi
+.ih
+REVISIONS
+.ls IMCOMBINE V2.12
+A number of enhancements for dealing with large numbers of images were
+made. Also the masktype option "!<keyword>", where <keyword> is a
+user specified keyword, was added.
+
+The new parameters "headers", "bpmasks", "rejmasks", "nrejmasks", and
+"expmasks" provide additional types of output. The old parameters
+"rejmask" and "plfile" were removed. The new "nrejmasks" corresponds
+to the old "plfile" and the new "rejmasks" corresponds to the old
+"rejmask".
+
+There is a new "combine" type "sum" for summing instead of averaging the
+final set of offset, scaled, and weighted pixels.
+
+there is a new parameter "outlimits" to allow output of a subregion of
+the full output. This is useful for raster surveys with large numbers
+of images.
+
+Additional keywords may appear in the output headers.
+
+The scaling is now done relative to the first image rather than an
+average over the images. This is done so that flux related keywords
+such as exposure time and airmass remain representative.
+.le
+.ls IMCOMBINE V2.11.2
+The grow algorithm was improved to give a 2D growing radius.
+
+An optional output mask file contains the identifications of which pixel
+in which input image was rejected or excluded.
+
+The internal calculation type was changed to be the highest precedence
+of the input and output types. Previously it was only the input types.
+.le
+.ls IMCOMBINE V2.11
+The limit of the number of images that may be combined has been removed.
+If the number of images exceeds the maximum number of open images permitted
+then the images are stacked in a single temporary image and then combined
+with the project option. Note that this will double the amount of
+diskspace temporarily. There is also a limitation in this case that the
+bad pixel mask from the first image in the list will be applied to all the
+images.
+
+Integer offsets may be determined from the image world coordinate system.
+
+A combination of ushort and short images now defaults to integer.
+.le
+.ls IMCOMBINE V2.14
+The "masktype" parameter has been generalized to allow both using a
+different keyword for the input mask and choosing the mask method.
+The "novalue" masktype is new and is useful for maintaining a distinction
+between no data and possibly marginally useful or cosmetically useful
+data.
+.le
+.ls IMCOMBINE V2.10.3
+The input scalings from an @file or header keyword are now truly
+mulitplicative or additive and they are not normalized. The output
+pixel types now include unsigned short integer.
+.le
+.ls IMCOMBINE V2.10.2
+The weighting was changed from using the square root of the exposure time
+or image statistics to using the values directly. This corresponds
+to variance weighting. Other options for specifying the scaling and
+weighting factors were added; namely from a file or from a different
+image header keyword. The \fInkeep\fR parameter was added to allow
+controlling the maximum number of pixels to be rejected by the clipping
+algorithms. The \fIsnoise\fR parameter was added to include a sensitivity
+or scale noise component to the noise model. Errors will now delete
+the output images.
+.le
+.ls IMCOMBINE V2.10
+This task was greatly revised to provide many new features. These features
+are:
+
+.nf
+ o Bad pixel masks
+ o Combining offset and different size images
+ o Blank value for missing data
+ o Combining across the highest dimension (the project option)
+ o Separating threshold rejection, the rejection algorithms,
+ and the final combining statistic
+ o New CCDCLIP, CRREJECT, and PCLIP algorithms
+ o Rejection now may reject more than one pixel per output pixel
+ o Choice of a central median or average for clipping
+ o Choice of final combining operation
+ o Simultaneous multiplicative and zero point scaling
+.fi
+.le
+.ih
+LIMITATIONS
+Though the previous limit on the number of images that can be combined
+was removed in V2.11 the method has the limitation that only a single
+bad pixel mask will be used for all images.
+.ih
+SEE ALSO
+ccdred.combine mscred.combine onedspec.scombine, wpfc.noisemodel,
+obsolete.ocombine
+.endhelp
diff --git a/pkg/images/immatch/doc/linmatch.hlp b/pkg/images/immatch/doc/linmatch.hlp
new file mode 100644
index 00000000..21c04b22
--- /dev/null
+++ b/pkg/images/immatch/doc/linmatch.hlp
@@ -0,0 +1,699 @@
+.help linmatch Apr95 images.immatch
+.ih
+NAME
+linmatch -- linearly match the intensity scales of 1 and 2D images
+.ih
+USAGE
+linmatch input reference regions lintransform
+.ih
+PARAMETERS
+.ls input
+The list of input images to be matched.
+.le
+.ls reference
+The list of reference images to which the input images are to be matched
+if \fIscaling\fR is one of the "mean", "median", "mode", or "fit"
+algorithms, or the list of reference photometry files if \fIscaling\fR
+specifies the "photometry" algorithm. The number of reference images or
+reference photometry files must be one or equal to the number of input
+images.
+.le
+.ls regions
+The list of image regions used to compute the intensity
+matching function if \fIscaling\fR is one of the "mean", "median", "mode",
+or "fit" algorithms, or a list of the input photometry files if
+\fIscaling\fR specifies the "photometry" algorithm. In the former
+case \fIregions\fR may be: 1) a string of the form "grid nx ny" defining
+a grid of nx by ny equally spaced and sized image regions spanning the
+entire image, 2) a list of object coordinates separated by commas e.g.
+"303 401, 131 202", 3) a list of image sections separated by whitespace
+e.g "[101:200,101:200] [301:400,301:400]",
+4) the name of a text file containing a list of object coordinates separated
+by newlines, and 5) the name of a text file containing a list of image
+sections separated by whitespace and/or newlines.
+.le
+.ls lintransform
+The name of the text file where the computed scaling factors are written.
+If \fIdatabasefmt\fR is "yes", a single record containing the computed
+bscale and bzero factors for each image region or object, and the
+average bscale and bzero, is written to the text database
+file for each input image. If \fIdatabasefmt\fR = "no", a single line
+containing the input image name, bscale factor, bzero factor, error
+in bscale, and error in bzero is written to a simple text file for
+each image.
+.le
+.ls output = ""
+The list of output matched images. If \fIoutput\fR is the NULL string
+then bscale and bzero are computed for each input image and written to
+\fIlintransform\fR, but no output images are written. If \fIoutput\fR
+is not NULL then the number of output images must equal the number of
+input images.
+.le
+.ls databasefmt = yes
+If \fIdatabasefmt\fR is "yes" the computed bscale and bzero factors
+are written to a text database file, otherwise they are written to a
+simple text file.
+.le
+.ls records = ""
+The list of records to be written to or read from \fIlintransform\fR one
+input image. If \fIrecords\fR is NULL then the output or input record names
+are assumed to be the names of the input images. If \fIrecords\fR is not NULL
+then the record names in \fIrecords\fR are used to write / read the
+database records. This parameter is useful for users
+who, wish to compute the bscale and bzero factors using images that have
+been processed
+in some manner (e.g. smoothed), but apply the computed bscale and bzero
+factors to the original unprocessed images. If more than one record
+with the same name exists in \fIlintransform\fR then the most recently written
+record takes precedence. The records parameter is ignored if
+\fIdatabasefmt\fR is "no".
+.le
+.ls append = yes
+Append new records to an existing \fIlintransform\fR file or start a new
+file for each execution of LINMATCH? The append parameter is
+ignored if \fIdatabasefmt\fR is "no".
+.le
+.ls shifts = ""
+An optional list of shifts files containing the x and y shifts to be applied
+to the reference regions to determine their positions in
+the input images. The number of shifts files must equal the number of
+reference images. The shifts are listed in the shifts file, 1 shift per line,
+with the x and y shifts in
+columns 1 and 2 respectively. If there are fewer x and y shifts defined
+in the shifts file than there are input images, the extra input
+images will be assigned x and y shifts of \fIxshift\fR and \fIyshift\fR
+respectively. The shifts parameter is ignored if the \fIscaling\fR
+parameter is set to "photometry".
+.le
+.ls xshift = 0.0 yshift = 0.0
+The default x and y shifts to be applied to the reference image regions
+or objects to compute their positions in the input image.
+Values in \fIshifts\fR take precedence over the values of \fIxshift\fR and
+\fIyshift\fR. xshift and yshift are ignored if the \fIscaling\fR parameter
+is set to "photometry".
+.le
+.ls dnx = 31 dny = 31
+The default size of a single image region used to compute the bscale
+and bzero factors if \fIscaling\fR is one of the "mean", "median", "mode",
+or "fit" algorithms and \fIregions\fR is a coordinate list rather than
+a sections list. dnx and dny are ignored if the \fIscaling\fR parameter
+is set to "photometry".
+.le
+.ls maxnregions = 100
+The maximum number of image regions or objects with measured photometry
+that can be used to compute the bscale and bzero factors.
+.le
+.ls scaling = "mean mean"
+The algorithms used to compute the bscale and bzero factors respectively.
+The options are:
+.ls mean median mode
+Bscale or bzero are computed using the "mean", "median", or "mode" statistic
+for each input and reference region individually. If one of the bscale or
+bzero fitting
+algorithms is set to "mean", "median", or "mode", the remaining factor
+must be set to "mean", "median" or "mode" or a numerical constant,
+e.g. "mean mean", "mean -100.0" or "2.63 mode".
+If both algorithms are set to "mean", "median", or "mode" bscale will be
+computed using the specified statistic and bzero will be set to 0.0
+If more than one input region is defined then a weighted least squares
+fit of the reference statistics to the input image statistics
+is performed and used to compute the final bscale and bzero factors.
+.le
+.ls fit
+Bscale and bzero are computed for each input image region individually
+by performing a least squares fit of the reference image pixels to
+the input image pixels. If more than one input image region is defined
+the final bscale and bzero factors are computed by averaging,
+weighted by their signal-to-noise ratios, the individual bscale and bzero
+values. If one of the bscale or bzero fitting
+algorithms is set to "fit", the remaining factor must either also
+be computed with the "fit" algorithm or set to a numerical constant,
+e.g. "fit fit", "fit -100.0", or "2.63 fit".
+.le
+.ls photometry
+Bscale and/or bzero are computed for each input object individually
+using photometry computed for a set of objects common to the reference
+and input images. If more than one input object is defined
+the final bscale and bzero factors are computed by averaging,
+weighted by their signal-to-noise ratios, the individual bscale and bzero
+values. If one of the bscale or bzero fitting
+algorithms is set to "photometry", the remaining factor must either also
+be computed with the "photometry" algorithm or set to a numerical
+constant, e.g. "photometry photometry", "photometry -100.0", or
+"2.63 photometry".
+.le
+.ls number
+Bscale and/or bzero are set to user defined numerical constants,
+e.g. "2.62 -55.0" or "2.62 median". If both bscale and bzero are numerical
+constants, LINMATCH must be run in non-interactive mode. If only one of bscale
+or bzero is a numerical constant, any of the "mean", "median", "mode", "fit",
+or "photometry" algorithms may be used to compute the remaining factor.
+.le
+.ls file
+Bscale and bzero are not computed but instead read from record \fIrecord\fR in
+the text database file \fIlintransform\fR if \fIdatabasefmt\fR is "yes",
+or the next line of a simple text file if \fIdatabasefmt\fR is "no".
+.le
+
+Further description of the matching algorithms can be found in the ALGORITHMS
+section.
+.le
+.ls datamin = INDEF datamax = INDEF
+The minimum and maximum good data values. Datamin and datamax are used by
+the "mean", "median", and "mode" scaling algorithms to reject entire
+image regions from the final fit, and by the "fit" algorithm to reject
+individual bad pixels from the least squares fits for the individual
+regions.
+.le
+.ls maxiter = 10
+The maximum number of iterations performed by the least squares fitting
+algorithm.
+.le
+.ls nreject = 0
+The maximum number of rejection cycles used to detect and reject bad pixels
+from the fit if the scaling algorithm is "fit" or bad regions / objects
+from the fit if the scaling algorithm is "mean", "median", "mode", "fit",
+or "photometry".
+.le
+.ls loreject = INDEF hireject = INDEF
+The high- and low-side bad data rejection limits used to detect and reject
+deviant pixels from the fit if the scaling algorithm is "fit" or bad
+regions / objects from the fit if the scaling algorithm is "mean", "median",
+"mode", "fit", or "photometry".
+.le
+.ls gain = "1.0 1.0" readnoise = "0.0 0.0"
+The reference and input image gain and readout noise in e-/ADU and
+e- respectively. Gain and readout may be numerical constants or the
+image header keyword containing the actual gain and/or readout noise
+value. Gain and readnoise are used by the "mean", "median", "mode",
+and "fit" algorithms to estimate the expected errors in the computed
+"mean", "median", or "mode" statistics, and by the "fit" algorithm
+to compute the per pixel errors values.
+.le
+.ls interactive = no
+Compute the bscale and bzero scaling factors for each image interactively
+using graphics cursor and optionally image cursor input.
+.le
+.ls verbose = yes
+Print messages about the progress of the task during task execution in
+non-interactive mode.
+.le
+.ls graphics = "stdgraph"
+The default graphics device.
+.le
+.ls display = "stdimage"
+The default image display device.
+.le
+.ls gcommands = ""
+The default graphics cursor.
+.le
+.ls icommands = ""
+The default image cursor.
+.le
+
+.ih
+DESCRIPTION
+
+LINMATCH computes the bscale and bzero factors required to match
+the intensity scales of a list of input
+images \fIinput\fR to the intensity scales of a list of reference
+images \fIreference\fR using the following definition of
+bscale and bzero and a variety of techniques.
+
+.nf
+ reference = bscale * input + bzero
+.fi
+
+The computed bscale and bzero factors are stored
+in the text file \fIlintransform\fR, in the record \fIrecords\fR if
+\fIdatabasefmt\fR is "yes", or a single line of a simple text file
+if \fIdatabasefmt\fR is "no". One record is written to the output file
+file for each input image. If a non NULL list of output images
+\fIoutput\fR is supplied, a scaled output image is written for
+each input image. LINMATCH is intended to solve 1D and 2D image intensity
+matching problems where the input and reference images: 1) have the same
+pixel scale and orientation, 2) differ in intensity by at most a scale
+factor and a zero point, and 3) contain one or more regions or objects in
+common that can be used to compute the scaling factors. Some of the scaling
+algorithms also require that the images registered and have identical
+point spread functions. LINMATCH cannot be used to compute or apply non-linear
+intensity matching functions.
+
+If \fIscaling\fR = "mean", "median", "mode", or "fit" bscale and bzero
+are computed directly from the input and reference image data using the
+image sections specified in the \fIregions\fR and one of the above fitting
+techniques as described in the ALGORITHMS section. All four algorithms
+require accurate knowledge of the measurement errors which in turn
+require accurate knowledge of the input and reference image gain and
+readout noise values. Gain and readout noise values can be entered by
+setting the \fIgain\fR and \fIreadnouse\fR parameters to the appropriate
+numerical values or image header keyword.
+
+\fIRegions\fR is interpreted as either: 1) a string of
+the form "grid nx ny" specifying a list of nx by ny image sections
+spanning the entire image, 2) a string defining the coordinates of a list
+of objects separated by commas e.g.
+"103.3 189.2, 204.4 389.7", 3) a string containing a list of image
+sections separated by whitespace, e.g "[100:203,200:300] [400:500,400:500]"
+4) the name of a text file containing the coordinates of one or
+more objects, one object per line, with the x and y coordinates
+in columns 1 and 2 respectively, 5) the name of a text
+file containing a list of image sections separated by whitespace and/or
+newlines. The image sections specifications, or alternatively
+the object coordinates and the parameters \fIdnx\fR and \fIdny\fR,
+determine the size of the input and reference image data regions to be
+extracted and used to compute the bscale and bzero factors.
+These image regions should be selected with care. Ideal regions
+span a range of intensity values and contain both object and background
+data.
+
+If \fIscaling\fR = "photometry", the bscale and bzero factors
+are computed directly from data in the input and reference image photometry
+files using the technique described in the ALGORITHMS section.
+In this case \fIregions\fR is a list of the input image photometry
+files and \fIreference\fR are the corresponding reference image
+photometry files written by a separate photometry task.
+These photometry files are simple text files with the object
+sky values, errors in the sky values, magnitudes, and errors in the
+magnitudes in columns 1, 2, 3, and 4 respectively.
+
+An image region is rejected from the fit if it contains data outside the
+limits specified by the \fIdatamin\fR and \fIdatamax\fR parameters
+and \fIscaling\fR =
+"mean", "median", or "mode". A pixel is rejected from the fit for an
+individual region if the pixel value is outside the limits specified
+by datamin and datamax, and the scaling algorithm is "fit". The datamin
+and datamax parameters are not used by the "photometry" scaling algorithm .
+
+Deviant pixels can be rejected from the fits to individual image regions
+if \fIscaling\fR = "fit", and \fInreject\fR, \fIloreject\fR, and
+\fIhireject\fR are set appropriately. Nreject, loreject and reject
+are also be used by all the scaling algorithms to reject image regions
+which contribute deviant bscale and bzero values.
+
+The computed bscale and bzero value for each region and the final bscale
+and bzero value for each input image are written to the linear
+transformation file \fIlintransform\fR.
+If \fIdatabasefmt\fR is "yes" each result is written to a record whose name
+is either identical to the name of the input
+image or supplied by the user via the \fIrecords\fR parameter .
+If \fIdatabasefmt\fR is "no", then a single line containing the input image
+name and the computed bscale and bzero values and their errors
+is written to the output shifts file.
+
+If a list of output image names have been supplied then the bscale and
+bzero values will be applied to the input images to compute the output images.
+
+If the \fIscaling\fR parameter is set to "file" then the shifts
+computed in a previous run of LINMATCH will be read from the \fIlintransform\fR
+file and applied to the input images to compute the output images.
+If no record list is supplied by the user LINMATCH will
+search for a record whose name is the same as the input image name. If more than
+one record of the same name is found then the most recently written
+record will be used.
+
+In non-interactive mode the task parameters are set at task startup time
+and the input images are processed sequentially. If the \fIverbose\fR
+flag is set, messages about the progress of the task are printed on the
+screen as the task is running.
+
+In interactive mode the user can mark the regions to be used
+to compute the matching function on the image display, show/set the data
+and algorithm parameters, compute, recompute, and plot
+matching function, and interactively delete and undelete
+bad data from the fits using the plots and graphics cursor. A summary
+of the available interactive commands is given in the CURSOR COMMANDS
+section.
+
+.ih
+CURSOR COMMANDS
+
+.nf
+The following graphics cursor commands are currently available in LINMATCH.
+
+ Interactive Keystroke Commands
+
+? Print help
+: Colon commands
+
+g Draw a plot of the current fit
+i Draw the residuals plot for the current fit
+p Draw a plot of current photometry
+s Draw histograms for the image region nearest the cursor
+l Draw the least squares fit for the image region nearest the cursor
+h Draw histogram plot of each image region in turn
+l Draw least squares fits plot of each image region in turn
+r Redraw the current plot
+d Delete the image region nearest the cursor
+u Undelete the image region nearest the cursor
+f Recompute the intensity matching function
+w Update the task parameters
+q Exit
+
+
+ Colon Commands
+
+:markcoords Mark objects on the display
+:marksections Mark image sections on the display
+:show Show current values of all the parameters
+
+ Show/set Parameters
+
+:input [string] Show/set the current input image
+:reference [string] Show/set the current reference image / phot file
+:regions [string] Show/set the current image regions
+:photfile [string] Show/set the current input photometry file
+:lintransform [string] Show/set the linear transform database file name
+:dnx [value] Show/set the default x size of an image region
+:dny [value] Show/set the default y size of an image region
+:shifts [string] Show/set the current shifts file
+:xshift [value] Show/set the input image x shift
+:yshift [value] Show/set the input image y shift
+:output [string] Show/set the current output image name
+:maxnregions Show the maximum number of objects / regions
+:gain [string] Show/set the gain value / image header keyword
+:readnoise [string] Show/set the readout noise value / image header
+ keyword
+
+:scaling Show the current scaling algorithm
+:datamin [value] Show/set the minimum good data value
+:datamax [value] Show/set the maximum good data value
+:nreject [value] Show/set the maximum number of rejection cycles
+:loreject [value] Show/set low side k-sigma rejection parameter
+:hireject [value] Show/set high side k-sigma rejection parameter
+.fi
+
+.ih
+ALGORITHMS
+
+MEAN, MEDIAN, AND MODE
+
+For each input and reference image region the mean, median, mode, statistic
+and an error estimate for that statistic are computed as shown below,
+mstat is for mean, median, or mode statistic, emstat stands for the error
+estimate, stdev for the measured standard deviation, and npix for the
+number of points.
+
+.nf
+ mstat = mean, median, or mode
+ emstat = min (sqrt (mean / gain + readnoise ** 2 / gain ** 2),
+ stdev / sqrt(npix))
+.fi
+
+If only a single image region is specified then mstat is used to compute
+one of bscale or bzero but not both as shown below. Bscale is computed by
+default.
+
+.nf
+ bscale = mstat[ref] / mstat[input]
+ err[bscale] = abs (bscale) * sqrt (emstat[ref] ** 2 / mstat[ref] ** 2 +
+ emstat[input] ** 2 / mstat[input] ** 2)
+ bzero = constant
+ err[bzero] = 0.0
+
+ bzero = mstat[ref] - mstat[input]
+ err[bzero] = sqrt (emstat[ref] ** 2 + emstat[input] ** 2)
+ bscale = constant
+ err[bscale] = 0.0
+.fi
+
+If more than one image region is defined then the computed mean, median,
+or mode values for the input and reference image regions are used as
+shown below to compute the bscale and bzero factors and their errors
+using a weighted least squares fit.
+
+.nf
+ mstat[ref] = bscale * mstat[input] + bzero
+.fi
+
+If an image region contains data outside the limits defined
+by \fIdatamin\fR and \fIdatamax\fR that image region is eliminated
+entirely from the fit.
+
+The parameters \fInreject\fR, \fIloreject\fR,
+and \fIhireject\fR are used to detect and automatically eliminate
+deviant data points from the final least squares fit. If for some reason
+bscale or bzero cannot be fit, default values of 1.0 and 0.0 are
+assigned.
+
+The mean, median, and mode algorithms depend on the global properties of
+the image regions. These algorithms do require the reference and
+input images to have the same pixel scale and orientation,
+but do not automatically require the reference and input images
+to have the same point spread function. Small shifts between the reference
+and input images can be removed using the \fIshifts\fR, \fIxshift\fR, and
+\fIyshift\fR parameters.
+
+If the image regions contain stars, then either regions should be large
+enough to include all the flux of the stars in which case the images
+do not have to have the same psf, or the psfs should be the same so
+that same portion of the psf is sampled. The best image regions for
+matching will contain object and background information.
+
+FIT
+
+For each input and reference image the bscale and bzero factors are
+computed by doing a pixel to pixel weighted least squares fit of the reference
+image counts to the input image counts as shown below.
+
+.nf
+ counts[ref] = bscale * counts[input] + bzero
+ weight = 1.0 / (err[ref] ** 2 + bscale ** 2 * err[input] ** 2)
+ err[ref] = sqrt (counts[ref] / gain[ref] + readnoise[ref] ** 2 /
+ gain[ref] ** 2)
+ err[input] = sqrt (counts[input] / gain[input] +
+ readnoise[input] ** 2 / gain[input] ** 2)
+.fi
+
+The fitting technique takes into account errors in both the reference and
+input image counts and provides an error estimate for the computed bscale
+and bzero factors. Bad data are rejected
+automatically from the fit by setting the \fIdatamin\fR and \fIdatamax\fR
+parameters. Deviant pixels are rejected from the fit by setting the
+\fInreject\fR, \fIloreject\fR, and \fIhireject\fR parameters appropriately.
+
+The final bscale and bzero for the input image are computed by calculating
+the average weighted by their errors of the individual bscale and bzero
+values. The parameters \fInreject\fR, \fIloreject\fR, and \fIhirject\fR
+can be used to automatically detect and reject deviant points.
+
+The fit algorithm depends on the results of pixel to pixel fits in
+each reference and input image region. The technique requires that the
+images be spatially registered and psfmatched before it is employed.
+Each input and reference image should contain a range of pixel intensities
+so that both bscale and bzero can be accurately determined.
+
+PHOTOMETRY
+
+For each object common to the reference and input photometry files
+the input sky values sky, errors in the sky values serr,
+magnitudes mag, and magnitude errors merr are used to compute the
+bscale and bzero factors and estimate their errors as shown
+below.
+
+.nf
+ bscale = 10.0 ** ((mag[ref] - mag[input]) / 2.5)
+ bzero = sky[ref] - bscale * sky[input]
+ err[bscale] = 0.4 * log(10.0) * bscale * sqrt (merr[ref] ** 2 +
+ magerr[input] ** 2))
+ err[bzero] = sqrt (serr[ref] ** 2 + err[bscale] ** 2 *
+ sky[input] ** 2 + bscale ** 2 * sky[input] ** 2)
+.fi
+
+The final bscale and bzero for the input image are computed by calculation
+the average of the individual bscale and bzero values weighted by their
+errors. The parameters \fInreject\fR, \fIloreject\fR, and \fIhirject\fR can
+be used to automatically detect and reject deviant points.
+
+THE LEAST SQUARES FITTING TECHNIQUE
+
+The least squares fitting code performs a double linear regression on
+the x and y points, taking into account the errors in both x and y.
+
+The best fitting line is the defined below.
+
+.nf
+ y = a * x + b
+.fi
+
+The error ellipses are
+
+.nf
+ S = (x - xfit) ** 2 / err[x] ** 2 + (y - yfit) ** 2 /
+ err[y] ** 2
+.fi
+
+where S is the quantity to be minimized. Initial values of a and b are
+estimated by fitting the data to a straight line assuming uniform
+weighting. The best fit values of a and b are then
+determined by iterating on the relationship
+
+.nf
+ dy = x' * da + db
+.fi
+
+where da and db are corrections to the previously determined values of a and
+b and dy and x' are defined as.
+
+.nf
+ dy = y - (ax + b)
+ x' = x + a * err[x] ** 2 * dy / (a ** 2 * err[x] ** 2 +
+ err[y] ** 2)
+.fi
+
+The new values of the a and b then become.
+
+.nf
+ a = a + da
+ b = b + db
+.fi
+
+.ih
+REFERENCES
+
+A review of doubly weighted linear regression problems in
+astronomy can be found in the paper "Linear Regression in Astronomy. II"
+by (Feigelson and Babu (1992 Ap.J. 397, 55). A detailed derivation of the
+particular solution used by LINMATCH can be found in the article
+"The Techniques of Least Squares and Stellar Photometry with CCDs"
+by Stetson (1989 Proceeding of the V Advanced School of Astrophysics,
+p 51).
+
+.ih
+EXAMPLES
+
+1. Match the intensity scales of a list of images to a reference
+image using a list of stars on the displayed reference image with
+the image cursor and the "mean" scaling algorithm. Assume that none
+of the stars are saturated and that a radius of 31 pixels is sufficient
+to include all the flux from the stars plus some background flux.
+Make sure that the correct gain and readout noise values are in the
+image headers.
+
+.nf
+ cl> display refimage 1
+
+ cl> rimcursor > objlist
+ ... mark several candidate stars by moving the cursor to the
+ star of interest and hitting the space bar key
+ ... type EOF to terminate the list
+
+ cl> linmatch @imlist refimage objlist lintran.db \
+ out=@outlist dnx=31 dny=31 scaling="mean mean" gain=gain \
+ readnoise=readnoise
+.fi
+
+2. Repeat the previous command but force the bzero factor to be -100.0
+instead of using the fitted value.
+
+.nf
+ cl> linmatch @imlist refimage objlist lintran.db \
+ out=@outlist dnx=31 dny=31 scaling="mean -100.0" \
+ gain=gain readnoise=rdnoise
+.fi
+
+3. Repeat the first example but compute bscale and bzero
+the bscale and bzero values using boxcar smoothed versions of
+the input images. Make sure the gain and readout noise are
+adjusted appropriately.
+
+.nf
+ cl> linmatch @bimlist brefimage objlist lintran.db \
+ dnx=31 dny=31 scaling="mean mean" gain=gain \
+ readnoise=rdnoise
+
+ cl> linmatch @imlist refimage objlist lintran.db \
+ out=@outimlist records=@bimlist scaling="file file"
+.fi
+
+4. Match the intensity of an input image which has been spatially
+registered and psfmatched to the reference image using the "fit" algorithm
+and a single reference image region. Remove the effects of saturated
+pixels by setting datamax to 28000 counts, and the effects of any deviant pixels
+by setting nreject, loreject, and hireject to appropriate values.
+
+.nf
+ cl> linmatch image refimage [50:150,50:150] lintran.db \
+ out=outimage scaling="fit fit" datamax=28000 nreject=3 \
+ loreject=3 hireject=3 gain=gain readnoise=rdnoise
+.fi
+
+5. Repeat the previous example but use several image sections to compute
+the bscale and bzero values.
+
+.nf
+ cl> linmatch image refimage sections lintran.db \
+ out=outimage scaling="fit fit" datamax=28000 nreject=3 \
+ loreject=3 hireject=3 gain=gain readnoise=rdnoise
+.fi
+
+6. Match the intensity scales of two images using photometry
+computed with the apphot package qphot task. The two images are
+spatially registered, psfmatched, and the photometry aperture is sufficient to
+include all the light from the stars. The filecalc task used to compute
+the error in the mean sky is in the addon ctio package.
+
+.nf
+ cl> display refimage 1 fi+
+ cl> rimcursor > objlist
+ ... mark several candidate stars by moving the cursor to the
+ star of interest and hitting the space bar key
+ ... type EOF to terminate the list
+ cl> qphot refimage coords=objlist inter-
+ cl> qphot image coords=objlist inter-
+ cl> pdump refimage.mag.1 msky,stdev,nsky,mag,merr yes | filecalc \
+ STDIN "$1;$2/sqrt($3);$4;$5" > refimage.phot
+ cl> pdump image.mag.1 msky,stdev,nsky,mag,merr yes | filecalc \
+ STDIN "$1;$2/sqrt($3);$4;$5" > image.phot
+ cl> linmatch image refimage.phot image.phot lintran.db \
+ out=outimage scaling="phot phot" nreject=3 loreject=3\
+ hireject=3
+.fi
+
+7. Register two images interactively using the fit algorithms and
+five non-overlapping image regions in the sections file.
+
+.nf
+ cl> linmatch image refimage sections lintran.db \
+ out=outimage scaling="fit fit" datamax=28000 nreject=3 \
+ loreject=3 hireject=3 gain=gain readnoise=rdnoise \
+ interactive +
+
+ ... a plot of bscale and bzero versus region number
+ appears
+
+ ... type ? to get a list of the keystroke and : commands
+
+ ... type i to see a plot of the bscale and bzero residuals
+ versus region
+
+ ... type g to return to the default bscale and bzero versus
+ region plot
+
+ ... type l to examine plot of the fits and residuals for the
+ individual regions
+ ... step forward and back in the regions list with the
+ space bar and -keys
+ ... flip back and forth between the fit and residuals
+ keys with l and i keys
+ ... return to the main plot by typing q
+
+ ... return to the residuals plot by typing i and delete a
+ region with a large residual by moving to the
+ bad point and typing d
+
+ ... type f to recompute the fit
+
+ ... type q to quit the interactive loop, n to go to the
+ next image or q to quit the task
+
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+imexpr, imcombine, ctio.filecalc, apphot.qphot, apphot.phot
+.endhelp
diff --git a/pkg/images/immatch/doc/psfmatch.hlp b/pkg/images/immatch/doc/psfmatch.hlp
new file mode 100644
index 00000000..4972700e
--- /dev/null
+++ b/pkg/images/immatch/doc/psfmatch.hlp
@@ -0,0 +1,595 @@
+.help psfmatch Oct94 images.immatch
+.ih
+NAME
+psfmatch -- match the point spread functions of 1 and 2D images
+.ih
+USAGE
+psfmatch input reference psfdata kernel
+.ih
+PARAMETERS
+.ls input
+The list of input images to be matched.
+.le
+.ls reference
+The list of reference images to which the input images are to be matched if
+\fIconvolution\fR = "image", or the list of reference image psfs if
+\fIconvolution\fR = "psf". The reference image psf must be broader than the
+input image psf in at least one dimension.
+The number of reference images/psfs must be one or equal to the number of
+input images.
+.le
+.ls psfdata
+The list of objects used to compute the psf matching function if
+\fIconvolution\fR is "image", or the list of input image psfs if
+\fIconvolution\fR is "psf". In the former case \fIpsfdata\fR may be:
+1) a string containing the x and y coordinates of a single object,
+e.g. "51.0 105.0" or 2) the name of a text file containing a list of
+objects, and the number of objects
+files must equal the number of reference images. In the latter case
+the number of input psf images must equal the number of input images.
+.le
+.ls kernel
+The list of input/output psf matching function images to be convolved with the
+input images to produce the output images. The number of kernel images
+must equal the number of input images.
+.le
+.ls output = ""
+The list of output matched images. If \fIoutput\fR is the NULL string
+then the psf matching function is computed for each input image and written to
+\fIkernel\fR but no output images are written. If \fIoutput\fR is not NULL
+then the number of output images must equal the number of input images.
+.le
+.ls convolution = "image"
+The algorithm used to compute the psf matching function. The options are:
+.ls image
+The psf matching function is computed directly from the reference and input
+image data using the objects specified in \fIpsfdata\fR, the data
+regions specified by \fIdnx\fR, \fIdny\fR, \fIpnx\fR, and \fIpny\fR,
+and the convolution theorem.
+.le
+.ls psf
+The psf matching function is computed directly from pre-computed
+reference and input image psfs using the convolution theorem.
+.le
+.ls kernel
+No psf matching function is computed. Instead the psf matching function
+is read from the input image \fIkernel\fR.
+.le
+.le
+.ls dnx = 31, ls dny = 31
+The x and y width of the data region to be extracted around each object. The
+data region should be big enough to include both object and sky data.
+\fIDnx\fR and \fIdny\fR are not used if \fIconvolution\fR is "psf" or
+"kernel".
+.le
+.ls pnx = 15, pny = 15
+The x and y width of the psf matching function to be computed which must be
+less than \fIdnx\fR and \fIdny\fR respectively. The psf
+matching function should be kept as small as possible to minimize
+the time required to compute the output image.
+\fIPnx\fR and \fIPny\fR are not used if \fIconvolution\fR is "psf" or
+"kernel".
+.le
+.ls center = yes
+Center the objects in \fIpsfdata\fR before extracting the data from the
+input and reference images. Centering should be turned off if the objects
+are non-stellar and do not have well-defined centers.
+Centering is turned off if \fIconvolution\fR is "psf" or
+"kernel".
+.le
+.ls background = median
+The default background function to be subtracted from the input
+and reference image data in each object region before the
+psf matching function is computed. The background is computed using
+data inside the data extraction region defined by \fIdnx\fR and \fIdny\fR
+but outside the kernel region defined by \fIpnx\fR and \fIpny\fR.
+Background fitting is turned off if \fIconvolution\fR is "psf" or
+"kernel".
+The options are:
+.ls none
+no background subtraction is done.
+.le
+.ls "insky refsky"
+the numerical values of insky and refsky are subtracted from the
+input and reference image respectively.
+.le
+.ls mean
+the mean of the input and reference image region is computed and subtracted
+from the image data.
+.le
+.ls median
+the median of the input and reference image region is computed and subtracted
+from the data.
+.le
+.ls plane
+a plane is fit to the input and reference image region and subtracted
+from the data.
+.le
+.le
+.ls loreject = INDEF, ls hireject = INDEF
+The k-sigma rejection limits for removing the effects of bad data from the
+background fit.
+.le
+.ls apodize = 0.0
+The fraction of the input and reference image data endpoints in x and y
+to apodize with a
+cosine bell function before the psf matching function is computed.
+Apodizing is turned off if \fIconvolution\fR is "psf" or
+"kernel".
+.le
+.ls fluxratio = INDEF
+The ratio of the integrated flux of the reference objects to the integrated
+flux of the input objects.
+By default \fIfluxratio\fR is computed directly from the input data.
+.le
+.ls filter = "replace"
+The filter used to remove high frequency noise from the psf
+matching function. Filtering is not performed if \fIconvolution\fR
+is "kernel". The options are:
+.ls cosbell
+apply a cosine bell taper to the psf matching function in frequency space.
+.le
+.ls replace
+replace the high-frequency low signal-to-noise components of the psf matching
+function with a gaussian model computed from the low frequency
+high signal-to-noise components of the matching function.
+.le
+.ls model
+replace the entire psf matching function with a gaussian model fit to the
+low frequency high signal-to-noise components of the matching function.
+.le
+.le
+.ls sx1 = INDEF, sx2 = INDEF, sy1 = INDEF, sy2 = INDEF
+The limits of the cosine bell taper in frequency space. Frequency components
+inside sx1 and sy1 are unaltered. Frequency components outside sx2 and sy2
+are set to 0.0. By default sx1 and sy1 are set to 0.0,
+and sx2 and sy2 are set to the largest frequency present in the data.
+.le
+.ls radsym = no
+Compute a radially symmetric cosine bell function ?
+.le
+.ls threshold = 0.2
+The low frequency cutoff in fraction of the total input image spectrum
+power for the filtering options "replace" and "model".
+.le
+.ls normfactor = 1.0
+The total power in the computed psf matching function \fIkernel\fR. By default
+the psf matching function is normalized. If \fInormfactor\fR
+is set to INDEF, then the total power is set to \fIfluxratio\fR.
+\fINormfactor\fR is not used if \fIconvolution\fR is set "kernel".
+.le
+.ls boundary_type = "nearest"
+The boundary extension algorithm used to compute the output matched
+image. The options are:
+.ls nearest
+use the value of the nearest boundary pixel.
+.le
+.ls constant
+use a constant value.
+.le
+.ls reflect
+generate a value by reflecting about the boundary.
+.le
+.ls wrap
+generate a value by wrapping around to the opposite side of the image.
+.le
+.le
+.ls constant = 0.0
+The default constant for constant boundary extension.
+.le
+.ls interactive = no
+Compute the psf matching function for each image
+interactively using graphics cursor and, optionally, image cursor input.
+.le
+.ls verbose
+Print messages about the progress of the task in non-interactive mode.
+.le
+.ls graphics = "stdgraph"
+The default graphics device.
+.le
+.ls display = "stdimage"
+The default image display device.
+.le
+.ls gcommands = ""
+The default graphics cursor.
+.le
+.ls icommands = ""
+The default image display cursor.
+.le
+
+.ih
+DESCRIPTION
+
+PSFMATCH computes the convolution kernel required to match the
+point-spread functions
+of the input images \fIinput\fR to the point-spread functions of
+the reference images \fIreference\fR using either the image data
+or pre-computed psfs and the convolution theorem.
+The computed psf matching functions are stored in the \fIkernel\fR images.
+If a non-NULL list of output images \fIoutput\fR is
+specified the input images are
+convolved with the kernel images to produce a list of psf matched output
+images. PSFMATCH requires
+that the input and reference images be spatially registered
+and that the reference images have poorer resolution (broader PSF)
+than the input images in at least one dimension.
+
+If \fIconvolution\fR = "image", the matching function is computed directly
+from the input and reference image data using the objects listed in
+\fIpsfdata\fR and the convolution theorem as described in the ALGORITHMS
+section. \fIpsfdata\fR is interpreted as either: 1) a
+string defining the coordinates of a single object e.g. "103.3 189.2" or 2)
+the name of a text file containing the coordinates of one or
+more objects, one object per line, with the x and y coordinates
+in columns 1 and 2 respectively. The object coordinates, the
+size of the data region to be extracted \fIdnx\fR
+by \fIdny\fR, and the size of the kernel to be computed \fIpnx\fR and
+\fIpny\fR, determine
+the input and reference image regions used to compute the psf matching
+function.
+These image regions should be selected with care. Ideal regions
+contain a single high signal-to-noise unsaturated star which has no close
+neighbors and is well centered on a pixel.
+
+If \fIcenter\fR is "yes" and \fIconvolution\fR is "image", the objects
+in \fIpsfdata\fR are centered before
+the data region is extracted. Centering should be on if the objects
+are stellar, particularly if their coordinates were read from the image
+display cursor. Centering should be off if the objects are non-stellar and
+do not have well-defined centers.
+
+If the \fIbackground\fR fitting algorithm is other than "none" and
+\fIconvolution\fR is "image", the background for each object is fit using
+data inside the region defined by
+\fIdnx\fR and \fIdny\fR but outside the region defined by
+\fIpnx\fR by \fIpny\fR. Bad data can be removed from the
+background fit by setting the parameters \fIloreject\fR and \fIhireject\fR.
+A cosine bell function is applied to the edges of the data region
+after background fitting but before computing the psf matching function
+if the \fIapodize\fR parameter is > 0.0.
+
+If \fIpsfdata\fR contains more than one object, the extracted image data
+is weighted by the total intensity in the extracted region after
+background subtraction, and averaged to produce a single smoothed
+data region for each reference and input image.
+
+If \fIconvolution\fR = "psf",
+the psf matching function is computed directly from the input image
+and reference
+image point-spread functions
+using the convolution theorem as described in the ALGORITHMS section.
+In this case \fIpsfdata\fR is the list of input image psfs and
+\fIreference\fR are the corresponding reference image psfs written by
+by some external psf modeling task.
+If \fIconvolution\fR is "psf",
+centering and background fitting
+are assumed to have been performed by the psf modeling task and are not
+performed by PSFMATCH.
+
+PSFMATCH requires that the total power in the psf matching function
+before normalization be the ratio
+of the integrated flux of the reference image/psf over the integrated
+flux of the input image/psf. If \fIfluxratio\fR is INDEF, PSFMATCH
+estimates this number internally as described in the ALGORITHMS section,
+otherwise the \fIfluxratio\fR is set to the value supplied by the user.
+
+If \fIconvolution\fR is "kernel", PSFMATCH reads the psf matching function
+from the images in \fIkernel\fR which were either
+created during a previous run of PSFMATCH or by a separate task.
+
+PSFMATCH provides several options for filtering out the ill-behaved
+noise-dominated high frequency components of the psf matching function
+that are produced when the ratio of reference / input image of psf
+fourier transforms is taken.
+
+If \fIfilter\fR is set to "cosbell", a cosine bell function
+with a taper defined by \fIsx1\fR, \fIsx2\fR, \fIsy1\fR, and \fIsy2\fR and
+symmetry defined by \fRradsym\fR is applied to
+the psf matching function in frequency space. This filter
+sets all the frequency components greater than \fIsx2\fR and \fIsy2\fR
+to 0.0 and leaves all frequency components inside \fIsx1\fR and \fIsy1\fR
+unaltered. Users should exercise this option with caution as the effect
+of the filtering process can be to significantly
+broaden the computed psf matching function as described in the ALGORITHMS
+section.
+
+An alternative approach to dealing with the noisy
+high frequency components of the psf
+matching function it is to replace them with a reasonable guess. If the
+matching function is approximately gaussian then its fourier transform is also
+approximately gaussian and the low frequency components can be modeled
+reliably with an elliptical gaussian function. The model derived from the low
+frequency components of the matching can then be used to replace the high
+frequency components.
+If \fIfilter\fR is set to "replace", those high frequency components
+of the matching function which have less than a fraction
+\fIthreshold\fR of their total power in the equivalent high frequency
+components of the divisor or input image transform,
+are replaced by a model computed by fitting a gaussian to the low frequency
+components of the matching function, as described in the ALGORITHMS section.
+If \fIfilter\fR = "model" then the entire psf matching function
+is replaced with the best fitting gaussian model.
+
+Another problem can arise during the computation of the psf matching
+function . Occasionally it is not possible by means of a single execution
+of PSFMATCH to match the reference and input image psfs. An example
+of this situation
+is the case where the seeing of the reference and input images
+was comparable but the declination guiding error in the reference
+image was larger than the error in the input image.
+In this case input image needs to be convolved to the resolution of
+the reference image. However it is also the case
+that the guiding error in ra in the input image is greater than the guiding
+error in ra in the reference image. In this case the reference image needs
+to be convolved to the resolution of the input image along the other axis.
+If no corrective action is taken by the task, the
+first time PSFMATCH is run the values of the psf matching function along
+the ra axis will be greater than the computed fluxratio, resulting in
+unrealistic action
+along this axis. PSFMATCH avoids this situation by internally limiting
+the psf matching function to a maximum value of fluxratio computed as described
+above.
+
+By default the psf matching function is normalized to unit power before
+output. This may not be what is desired since if carefully computed the
+internally computed quantity a contains information about differences
+in exposure time, transparency, etc. If \fInormfactor\fR is set to
+a number of INDEF, the total power of the psf matching function will be
+set to that value of \fIfluxratio\fR respectively.
+
+If a list of output images names has been supplied then the computed
+psf matching function is applied to the input images to produce
+the output images using the boundary extension algorithm
+defined by \fIboundary\fR and \fIconstant\fR.
+
+In non-interactive mode the parameters are set at task startup time and
+the input images are processed sequentially. If the \fIverbose\fR flag
+is set messages about the progress of the task are printed on he
+screen as the task is running.
+
+In interactive mode the user can mark the regions to be used to compute
+the psf matching function on the image display, show/set the data
+and algorithm parameters, compute, recompute, and plot the psf matching
+function and its accompanying fourier spectrum, and experiment with the
+various filtering and modeling options.
+
+.ih
+CURSOR COMMANDS
+
+The following graphics cursor commands are currently available in
+PSFMATCH.
+
+.nf
+ Interactive Keystroke Commands
+
+
+? Print help
+: Colon commands
+k Draw a contour plot of the psf matching kernel
+p Draw a contour plot of the psf matching kernel fourier spectrum
+x Draw a column plot of the psf matching kernel / fourier spectrum
+y Draw a line plot of the psf matching kernel / fourier spectrum
+r Redraw the current plot
+f Recompute the psf matching kernel
+w Update the task parameters
+q Exit
+
+
+ Colon Commands
+
+
+:mark [file] Mark objects on the display
+:show Show current values of the parameters
+
+
+ Show/Set Parameters
+
+
+:input [string] Show/set the current input image name
+:reference [string] Show/set the current reference image/psf name
+:psf [file/string] Show/set the objects/input psf list
+:psfimage [string] Show/set the current input psf name
+:kernel [string] Show/set the current psf matching kernel name
+:output [string] Show/set the current output image name
+
+:dnx [value] Show/set x width of data region(s) to extract
+:dny [value] Show/set y width of data region(s) to extract
+:pnx [value] Show/set x width of psf matching kernel
+:pny [value] Show/set y width of psf matching kernel
+:center [yes/no] Show/set the centering switch
+:background [string] Show/set the background fitting function
+:loreject [value] Show/set low side k-sigma rejection parameter
+:hireject [value] Show/set high side k-sigma rejection parameter
+:apodize [value] Show/set percent of endpoints to apodize
+
+:filter [string] Show/set the filtering algorithm
+:fluxratio [value] Show/set the reference/input psf flux ratio
+:sx1 [value] Show/set inner x frequency for cosbell filter
+:sx2 [value] Show/set outer x frequency for cosbell filter
+:sy1 [value] Show/set inner y frequency for cosbell filter
+:sy2 [value] Show/set outer y frequency for cosbell filter
+:radsym [yes/no] Show/set radial symmetry for cosbell filter
+:threshold [value] Show/set %threshold for replace/modeling filter
+:normfactor [value] Show/set the kernel normalization factor
+.fi
+
+.ih
+ALGORITHMS
+
+The problem of computing the psf matching function can expressed
+via the convolution theorem as shown below.
+In the following expressions r is the reference
+image data or reference image psf, i is the input image data or input image
+psf, k is the unit power psf matching
+function,
+a is a scale factor specifying the ratio of the total
+power in the reference data or psf to the total power in the input data or
+psf, * is the convolution operator, and FT is the fourier transform operator.
+
+.nf
+ r = ak * d
+ R = FT (r)
+ I = FT (i)
+ aK = R / I
+ ak = FT (aK)
+.fi
+
+The quantity ak is the desired psf matching function and aK is its fourier
+transform.
+
+If the background was accurately removed from the image or psf data before the
+psf matching function was computed, the quantity a is simply the central
+frequency component of the computed psf matching function aK as shown below.
+
+.nf
+ aK[0,0] = a = sum(r) / sum(i)
+.fi
+
+If the background was not removed from the image or psf data before the
+psf matching function was computed the previous expression is not valid.
+The computed aK[0,0] will include an offset and a must be estimated
+in some other manner. The approach taken by PSFMATCH in this circumstance
+is to fit a gaussian model to the absolute value of 1st and 2nd frequencies
+of R and I along the x and y axes independently, average the fitted x and y
+amplitudes, and set aK[0,0] to the ratio of the resulting fitted amplitudes
+as shown below.
+
+.nf
+ a = amplitude (R) / amplitude (I)
+ = (sum(r) - sum(skyr)) / (sum(i) - sum(skyi))
+ aK[0,0] = a
+.fi
+
+This approach will work well as long as the image data or psf is reasonably
+gaussian but may not work well in arbitrary image regions. If the user is
+dissatisfied with either of the techniques described above they can
+set aK[0,0] to a pre-determined value of their own.
+
+If a filter is applied to the computed psf matching function in frequency
+space then instead of computing
+
+.nf
+ ak = FT (aK)
+.fi
+
+PSFMATCH actually computes
+
+.nf
+ ak' = FT (aKF) = ak * f
+.fi
+
+where F is the applied filter in frequency space and f is its
+fourier transform. Care should be taken in applying any filter.
+For example if F is the step function, then ak' will be the desired kernel
+ak convolved with f, a sinc function of frequency 2 * PI / hwidth where
+hwidth is the half-width of the step function, and the resulting k'
+will be too broad.
+
+If the user chooses to replace the high frequency components of the psf
+matching function with a best guess, PSFMATCH performs the following
+steps:
+
+.nf
+1) fits an elliptical gaussian to those frequency components of the fourier
+spectrum of aK for which for which the amplitude of I is greater
+than threshold * I[0,0] to determine the geometry of the ellipse
+
+2) uses the fourier shift theorem to preserve the phase information in the
+model and solve for any x and y shifts
+
+3) replace those frequency components of aK for which the fourier spectrum
+of I is less than threshold * I[0,0] with the model values
+
+ or alternatively
+
+replace all of aK with the model values
+.fi
+
+.ih
+EXAMPLES
+
+1. Psf match a list of input images taken at different epochs with variable
+seeing conditions to a reference image with the poorest seeing by marking
+several high signal-to-noise isolated stars on the displayed reference image
+and computing the psf matching function directly from the input and reference
+image data. User makes two runs with psfmatch one to compute and check the
+kernel images and one to match the images.
+
+.nf
+ cl> display refimage 1 fi+
+
+ cl> rimcursor > objects
+
+ cl> psfmatch @inimlist refimage objects @kernels dnx=31 \
+ dny=31 pnx=15 pny=15
+
+ cl> imstat @kernels
+
+ cl> psfmatch @inlist refimage objects @kernels \
+ output=@outlist convolution="kernel"
+.fi
+
+2. Psf match two spectra using a high signal-to-noise portion of the
+data in the middle of the spectrum. Since the spectra are registered
+spatially and there is little data available for background fitting the
+user chooses to turn centering off and set the backgrounds manually.
+
+.nf
+ cl> psfmatch inspec refspec "303.0 1.0" kernel \
+ output=outspec dnx=31 dny=31 pnx=15 pny=15 center- \
+ back="403.6 452.0"
+.fi
+
+3. Psf match two images using psf functions inpsf and refpsf computed with
+the daophot package phot/psf/seepsf tasks. Since the kernel is fairly
+large use the stsdas fourier package task fconvolve to do the actual
+convolution. The boundary extension algorithm in fconvolve is equivalent
+to setting the psfmatch boundary extension parameters boundary and
+constant to "constant" and "0.0" respectively.
+
+.nf
+ cl> psfmatch inimage refpsf inpsf kernel convolution=psf
+
+ cl> fconvolve inimage kernel outimage
+.fi
+
+4. Psf match two images interactively using the image data itself to
+compute the psf matching function.
+
+.nf
+ cl> psfmatch inimage refimage objects kernel interactive+
+
+ ... a contour plot of the psf matching function appears
+ with the graphics cursor ready to accept commands
+
+ ... type x and y to get line and column plots of the psf
+ matching function at various points and k to return
+ to the default contour plot
+
+ ... type ? to get a list of the available commands
+
+ ... type :mark to define a new set of objects
+
+ ... type f to recompute the psf matching function using
+ the new objects
+
+ ... increase the data window to 63 pixels in x and y
+ with the :dnx 63 and :dny 63 commands, at the
+ same time increase the psf function size to 31 with
+ the colon commands :pnx 31 and :pny 31
+
+ ... type f to recompute the psf matching function using
+ the new data and kernel windows
+
+ ... type q to quit the task, and q again to verify the previous
+ q command
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+convolve, gauss, stsdas.fconvolve, digiphot.daophot.psf
+.endhelp
diff --git a/pkg/images/immatch/doc/skymap.hlp b/pkg/images/immatch/doc/skymap.hlp
new file mode 100644
index 00000000..b1a4a3fc
--- /dev/null
+++ b/pkg/images/immatch/doc/skymap.hlp
@@ -0,0 +1,642 @@
+.help skymap Dec96 images.immatch
+.ih
+NAME
+skymap -- compute the spatial transformation function required to register
+a list of images using celestial coordinate WCS information
+.ih
+USAGE
+skymap input reference database
+.ih
+PARAMETERS
+.ls input
+The list of input images containing the input celestial coordinate wcs.
+.le
+.ls reference
+The list of reference images containing the reference celestial coordinate
+wcs. The number of reference images must be one or equal to the number
+of input images.
+.le
+.ls database
+The name of the output text database file containing the computed
+transformations.
+.le
+.ls transforms = ""
+An option transform name list. If transforms is undefined then the
+transforms are assigned record names equal to the input image names.
+.le
+.ls results = ""
+Optional output files containing a summary of the results including a
+description of the transform geometry and a listing of the input coordinates,
+the fitted coordinates, and the fit residuals. The number of results files
+must be one or equal to the number of input files. If results is "STDOUT" the
+results summary is printed on the standard output.
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum logical x and logical y coordinates used to generate
+the grid of reference image control points and define the region of
+validity of the spatial transformation. Xmin, xmax, ymin, and
+ymax are assigned defaults of 1, the number of columns in the reference
+image, 1, and the number of lines in the reference image, respectively.
+.le
+.ls nx = 10, ny = 10
+The number of points in x and y used to generate the coordinate grid.
+.le
+.ls wcs = "world"
+The world coordinate system of the coordinates. The options are:
+.ls physical
+Physical coordinates are pixel coordinates which are invariant with
+respect to linear transformations of the physical image data. For example,
+if the reference
+image is a rotated section of a larger input image, the physical
+coordinates of an object in the reference image are equal to the physical
+coordinates of the same object in the input image, although the logical
+pixel coordinates are different.
+.le
+.ls world
+World coordinates are image coordinates which are invariant with
+respect to linear transformations of the physical image data and which
+are in degrees for all celestial coordinate
+systems. Obviously if the
+wcs is correct the ra and dec of an object
+should remain the same no matter how the image
+is linearly transformed. The default world coordinate
+system is either 1) the value of the environment variable "defwcs" if
+set in the user's IRAF environment (normally it is undefined) and present
+in the image header, 2) the value of the "system"
+attribute in the image header keyword WAT0_001 if present in the
+image header or, 3) the "physical" coordinate system.
+.le
+.le
+.ls xformat = "%10.3f", yformat = "%10.3f"
+The format of the output logical x and y reference and input pixel
+coordinates in columns 1 and 2 and 3 and 4 respectively. By default the
+coordinates are output right justified in a field of ten spaces with
+3 digits following the decimal point.
+.le
+.ls rwxformat = "", rwyformat = ""
+The format of the output reference image celestial coordinates
+in columns 5 and 6 respectively. The internal default formats will give
+reasonable output formats and precision for all celestial coordinate
+systems.
+.le
+.ls wxformat = "", wyformat = ""
+The format of the output input image celestial coordinates
+in columns 7 and 8 respectively. The internal default formats will give
+reasonable output formats and precision for all celestial coordinate
+systems.
+.le
+.ls fitgeometry = "general"
+The fitting geometry to be used. The options are the following.
+.ls shift
+X and y shifts only are fit.
+.le
+.ls xyscale
+X and y shifts and x and y magnification factors are fit. Axis flips are
+allowed for.
+.le
+.ls rotate
+X and y shifts and a rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rscale
+X and y shifts, a magnification factor assumed to be the same in x and y, and a
+rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rxyscale
+X and y shifts, x and y magnifications factors, and a rotation angle are fit.
+Axis flips are allowed for.
+.le
+.ls general
+A polynomial of arbitrary order in x and y is fit. A linear term and a
+distortion term are computed separately. The linear term includes an x and y
+shift, an x and y scale factor, a rotation and a skew. Axis flips are also
+allowed for in the linear portion of the fit. The distortion term consists
+of a polynomial fit to the residuals of the linear term. By default the
+distortion terms is set to zero.
+.le
+
+For all the fitting geometries except "general" no distortion term is fit,
+i.e. the x and y polynomial orders are assumed to be 2 and the cross term
+switches are set to "none" regardless of the values of the \fIxxorder\fR,
+\fIxyorder\fR, \fIxxterms\fR, \fIyxorder\fR, \fIyyorder\fR and \fIyxterms\fR
+parameters set by the user.
+.le
+.ls function = "polynomial"
+The type of analytic coordinate surfaces to be fit. The options are the
+following.
+.ls legendre
+Legendre polynomials in x and y.
+.le
+.ls chebyshev
+Chebyshev polynomials in x and y.
+.le
+.ls polynomial
+Power series polynomials in x and y.
+.le
+.le
+.ls xxorder = 2, xyorder = 2, yxorder = 2, yyorder = 2
+The order of the polynomials in x and y for the x and y fits respectively.
+The default order and cross term settings define the linear term in x
+and y, where the 6 coefficients can be interpreted in terms of an x and y shift,
+an x and y scale change, and rotations of the x and y axes. The "shift",
+"xyscale", "rotation", "rscale", and "rxyscale", fitting geometries
+assume that the polynomial order parameters are 2 regardless of the values
+set by the user. If any of the order parameters are higher than 2 and
+\fIfitgeometry\fR is "general", then a distortion surface is fit to the
+residuals from the linear portion of the fit.
+.le
+
+.ls xxterms = "half", yxterms = "half"
+The options are:
+.ls none
+The individual polynomial terms contain powers of x or powers of y but not
+powers of both.
+.le
+.ls half
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1, xyorder - 1) for the x fit and
+MAX (yxorder - 1, yyorder - 1) for the y fit.
+.le
+.ls full
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1 + xyorder - 1) for the x fit and
+MAX (yxorder - 1 + yyorder - 1) for the y fit.
+.le
+
+The "shift", "xyscale", "rotation", "rscale", and "rxyscale" fitting
+geometries, assume that the cross term switches are set to "none"regardless
+of the values set by the user. If either of the cross terms parameters is
+set to "half" or "full" and \fIfitgeometry\fR is "general" then a distortion
+surface is fit to the residuals from the linear portion of the fit.
+.le
+
+.ls reject = INDEF
+The rejection limit in units of sigma. The default is no rejection.
+.le
+.ls calctype = "real"
+The precision of coordinate transformation calculations. The options are "real"
+and "double".
+.le
+.ls verbose = yes
+Print messages about the progress of the task?
+.le
+.ls interactive = yes
+Run the task interactively ?
+In interactive mode the user may interact with the fitting process, e.g.
+change the order of the fit, delete points, replot the data etc.
+.le
+.ls graphics = "stdgraph"
+The graphics device.
+.le
+.ls gcommands = ""
+The graphics cursor.
+.le
+
+.ih
+DESCRIPTION
+
+SKYMAP computes the spatial transformation function required to map the
+celestial coordinate system of the reference image \fIreference\fR to
+the celestial coordinate
+system of the input image \fIinput\fR, and stores the computed function in
+the output text database file \fIdatabase\fR.
+The input and reference images may be 1D or 2D but
+must have the same dimensionality. The input image and output
+text database file can be input to the REGISTER or GEOTRAN tasks to
+perform the actual image registration. SKYMAP assumes that the world
+coordinate systems in the input and reference
+image headers are accurate and that the two systems are compatible, e.g. both
+images have a celestial coordinate system WCS.
+
+SKYMAP computes the required spatial transformation by matching the logical
+x and y pixel coordinates of a grid of points
+in the input image with the logical x and y pixels coordinates
+of the same grid of points in the reference image,
+using celestial coordinate information stored in the two image headers.
+The coordinate grid consists of \fInx * ny\fR points evenly distributed
+over the logical pixel space of interest in the reference image defined by the
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR, \fIymax\fR parameters.
+The logical x and y reference image pixel coordinates are transformed to
+reference image celestial coordinates using
+world coordinate information stored in the reference image header.
+The reference image celestial coordinates are transformed to
+input image celestial coordinates using world coordinate
+system information in both the reference and the input image headers.
+Finally the input image celestial coordinates are transformed to logical x and y
+input image pixel coordinates using world coordinate system information
+stored in the input image header. The transformation sequence looks
+like the following for an equatorial celestial coordinate system:
+
+.nf
+ (x,y) reference -> (ra,dec) reference (reference image wcs)
+(ra,dec) reference -> (ra,dec) input (reference and input image wcs)
+ (ra,dec) input -> (x,y) input (input image wcs)
+.fi
+
+The computed reference and input logical coordinates and the
+world coordinates are written to temporary coordinates file which is
+deleted on task termination.
+The pixel and celestial coordinates are written using
+the \fIxformat\fR and \fIyformat\fR and the \fIrwxformat\fR, \fIrwyformat\fR,
+\fIwxformat\fR and \fIwxformat\fR
+parameters respectively. If these formats are undefined and, in the
+case of the celestial coordinates a format attribute cannot be
+read from either the reference or the input images, reasonable default
+formats are chosen.
+If the reference and input images are 1D then all the output logical and
+world y coordinates are set to 1.
+
+SKYMAP computes a spatial transformation of the following form.
+
+.nf
+ xin = f (xref, yref)
+ yin = g (xref, yref)
+.fi
+
+The functions f and g are either a power series polynomial or a Legendre or
+Chebyshev polynomial surface of order \fIxxorder\fR and \fIxyorder\fR in x
+and \fIyxorder\fR and \fIyyorder\fR in y.
+
+Several polynomial cross terms options are available. Options "none",
+"half", and "full" are illustrated below for a quadratic polynomial in
+x and y.
+
+.nf
+xxterms = "none", xyterms = "none"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a13' * yref ** 2
+
+xxterms = "half", xyterms = "half"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a22 * xref * yref + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a22' * xref * yref + a13' * yref ** 2
+
+xxterms = "full", xyterms = "full"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a31 * xref ** 2 +
+ a12 * yref + a22 * xref * yref + a32 * xref ** 2 * yref +
+ a13 * yref ** 2 + a23 * xref * yref ** 2 +
+ a33 * xref ** 2 * yref ** 2
+ yin = a11' + a21' * xref + a31' * xref ** 2 +
+ a12' * yref + a22' * xref * yref + a32' * xref ** 2 * yref +
+ a13' * yref ** 2 + a23' * xref * yref ** 2 +
+ a33' * xref ** 2 * yref ** 2
+.fi
+
+If the \fBfitgeometry\fR parameter is anything other than "general", the
+order parameters assume the value 2 and the cross terms switches assume
+the value "none", regardless of the values set by the user. The computation
+can be done in either real or double precision by setting the \fIcalctype\fR
+parameter. Automatic pixel rejection may be enabled by setting the \fIreject\fR
+parameter to a positive number other than INDEF.
+
+The transformation computed by the "general" fitting geometry is arbitrary
+and does not necessarily correspond to a physically meaningful model.
+However the computed
+coefficients for the linear term can be given a simple geometrical geometric
+interpretation for all the fitting geometries as shown below.
+
+.nf
+ fitting geometry = general (linear term)
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+
+ fitting geometry = shift
+ xin = a + xref
+ yin = d + yref
+
+ fitting geometry = xyscale
+ xin = a + b * xref
+ yin = d + f * yref
+
+ fitting geometry = rotate
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/-1
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rxyscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+.fi
+
+
+The coefficients can be interpreted as follows. Xref0, yref0, xin0, yin0
+are the origins in the reference and input frames respectively. Orientation
+and skew are the orientation of the x and y axes and their deviation from
+perpendicularity respectively. Xmag and ymag are the scaling factors in x and
+y and are assumed to be positive.
+
+.nf
+ general (linear term)
+ xrotation = rotation - skew / 2
+ yrotation = rotation + skew / 2
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ shift
+ xrotation = 0.0, yrotation = 0.0
+ xmag = ymag = 1.0
+ b = 1.0
+ c = 0.0
+ e = 0.0
+ f = 1.0
+ a = xin0 - xref0 = xshift
+ d = yin0 - yref0 = yshift
+
+ xyscale
+ xrotation 0.0 / 180.0 yrotation = 0.0
+ b = + /- xmag
+ c = 0.0
+ e = 0.0
+ f = ymag
+ a = xin0 - b * xref0 = xshift
+ d = yin0 - f * yref0 = yshift
+
+ rscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ mag = xmag = ymag
+ const = mag * mag
+ b = mag * cos (xrotation)
+ c = mag * sin (yrotation)
+ e = -mag * sin (xrotation)
+ f = mag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ rxyscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ const = xmag * ymag
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+.fi
+
+
+\fIXmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR define the region of
+validity of the fit as well as the limits of the grid
+in the reference coordinate system. These parameters are also used to
+reject out of range data before the actual fitting is done.
+
+Each computed transformation is written to the output file \fIdatabase\fR
+in a record whose name is supplied by the user via the \fItransforms\fR
+parameter or set to the name of the corresponding input image.
+The database file is opened in append mode and new records are written
+to the end of the existing file. If more that one record of the same
+name is written to the database file, the last record written is the
+valid record, i.e. the one that will be used by the REGISTER or
+GEOTRAN tasks.
+
+SKYMAP will terminate with an error if the reference and input images
+are not both either 1D or 2D.
+If the celestial coordinate system information cannot be read from either
+the reference or input image header, the requested transformations
+from the celestial <-> logical coordinate systems cannot be compiled for either
+or both images, or the celestial coordinate systems of the reference and input
+images are fundamentally incompatible in some way, the output logical
+reference and input image coordinates are both set to a grid of points
+spanning the logical pixel space of the input, not the reference image.
+This grid of points defines an identity transformation which will leave
+the input image unchanged if applied by the REGISTER or GEOTRAN tasks.
+
+If \fIverbose\fR is "yes" then messages about the progress of the task
+as well as warning messages indicating potential problems are written to
+the standard output. If \fIresults\fR is set to a file name then the input
+coordinates, the fitted coordinates, and the residuals of the fit are
+written to that file.
+
+SKYMAP may be run interactively by setting the \fIinteractive\fR
+parameter to "yes".
+In interactive mode the user has the option of viewing the fit, changing the
+fit parameters, deleting and undeleting points, and replotting
+the data until a satisfactory
+fit has been achieved.
+
+.ih
+CURSOR COMMANDS
+
+In interactive mode the following cursor commands are currently available.
+
+.nf
+ Interactive Keystroke Commands
+
+? Print options
+f Fit the data and graph with the current graph type (g, x, r, y, s)
+g Graph the data and the current fit
+x,r Graph the x fit residuals versus x and y respectively
+y,s Graph the y fit residuals versus x and y respectively
+d,u Delete or undelete the data point nearest the cursor
+o Overplot the next graph
+c Toggle the constant x, y plotting option
+t Plot a line of constant x, y through the nearest data point
+l Print xshift, yshift, xmag, ymag, xrotate, yrotate
+q Exit the interactive curve fitting
+.fi
+
+The parameters listed below can be changed interactively with simple colon
+commands. Typing the parameter name alone will list the current value.
+
+.nf
+ Colon Parameter Editing Commands
+
+:show List parameters
+:fitgeometry Fitting geometry (shift,xyscale,rotate,
+ rscale,rxyscale,general)
+:function [value] Fitting function (chebyshev,legendre,
+ polynomial)
+:xxorder :xyorder [value] X fitting function xorder, yorder
+:yxorder :yyorder [value] Y fitting function xorder, yorder
+:xxterms :yxterms [n/h/f] X, Y fit cross terms type
+:reject [value] Rejection threshold
+.fi
+
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+REFERENCES
+
+Additional information on IRAF world coordinate systems including
+more detailed descriptions of the "logical", "physical", and "world"
+coordinate systems can be found in the help pages for the WCSEDIT
+and WCRESET tasks. Detailed documentation for the IRAF world
+coordinate system interface MWCS can be found in the file
+"iraf$sys/mwcs/MWCS.hlp". This file can be formatted and printed
+with the command "help iraf$sys/mwcs/MWCS.hlp fi+ | lprint".
+
+Details of the FITS header world coordinate system interface can
+be found in the draft paper "World Coordinate Systems Representations Within the
+FITS Format" by Hanisch and Wells, available from the iraf anonymous ftp
+archive and the draft paper which supersedes it "Representations of Celestial
+Coordinates in FITS" by Greisen and Calabretta available from the NRAO
+anonymous ftp archives.
+
+The spherical astronomy routines employed here are derived from the Starlink
+SLALIB library provided courtesy of Patrick Wallace. These routines
+are very well documented internally with extensive references provided
+where appropriate. Interested users are encouraged to examine the routines
+for this information. Type "help slalib" to get a listing of the SLALIB
+routines, "help slalib opt=sys" to get a concise summary of the library,
+and "help <routine>" to get a description of each routine's calling sequence,
+required input and output, etc. An overview of the library can be found in the
+paper "SLALIB - A Library of Subprograms", Starlink User Note 67.7
+by P.T. Wallace, available from the Starlink archives.
+
+.ih
+EXAMPLES
+
+1. Compute the spatial transformation required to match a radio image to an
+X-ray image of the same field using a 100 point coordinate grid
+and a simple linear transformation. Both images have accurate sky
+equatorial world coordinate systems define at different equinoxes.
+Print the output world coordinates
+in the coords file in hh:mm:ss.ss and dd:mm:ss.s format. Run geotran
+on the results to do the actual registration.
+
+.nf
+ cl> skymap radio xray geodb rwxformat=%12.2H rwyformat=%12.1h \
+ wxformat=%12.2H wyformat=%12.1h interactive-
+
+ cl> geotran radio radio.tran geodb radio
+.fi
+
+2. Repeat the previous command but begin with a higher order fit
+and run the task in interactive mode in order to examine the fit
+residuals.
+
+.nf
+ cl> skymap radio xray geodb rwxformat=%12.2H rwyformat=%12.1h \
+ wxformat=%12.2H wyformat=%12.1h xxo=4 xyo=4 xxt=half \
+ yxo=4 yyo=4 yxt=half
+
+ ... a plot of the fit appears
+
+ ... type x and r to examine the residuals of the x
+ surface fit versus x and y
+
+ ... type y and s to examine the residuals of the y
+ surface fit versus x and y
+
+ ... delete 2 deviant points with the d key and
+ recompute the fit with the f key
+
+ ... type q to quit and save the fit
+
+ cl> geotran radio radio.tran geodb radio
+.fi
+
+3. Repeat example 1 but set the transform name specifically.
+
+.nf
+ cl> skymap radio xray geodb trans=m82 rwxformat=%12.2H \
+ rwyformat=%12.1h wxformat=%12.2H wyformat=%12.1h \
+ interactive-
+
+ cl> geotran radio radio.tran geodb m82
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+wcsctran,register,geotran
+.endhelp
diff --git a/pkg/images/immatch/doc/skyxymatch.hlp b/pkg/images/immatch/doc/skyxymatch.hlp
new file mode 100644
index 00000000..63485284
--- /dev/null
+++ b/pkg/images/immatch/doc/skyxymatch.hlp
@@ -0,0 +1,406 @@
+.help skyxymatch Dec96 images.immatch
+.ih
+NAME
+skyxymatch -- match input and reference image x-y coordinates using the
+celestial coordinate WCS
+.ih
+USAGE
+skyxymatch input reference output
+.ih
+PARAMETERS
+.ls input
+The list of input images containing the input celestial coordinate wcs.
+.le
+.ls reference
+The list of reference images containing the reference celestial coordinate
+wcs. The number of reference images must be one or equal to the number
+of input images.
+.le
+.ls output
+The output matched coordinate lists containing:
+1) the logical x-y pixel coordinates of a point
+in the reference image in columns 1 and 2, 2) the logical x-y pixel
+coordinates of the same point in the input image in columns 3 and 4,
+3) the world coordinates of the point in the reference image in columns
+5 and 6, and 4) the world coordinate of the point in the input image in
+columns 7 and 8. The output coordinate list can be
+input directly to the geomap task. The number of output files must be
+equal to the number of input images or be the standard output STDOUT.
+.le
+.ls coords = "grid"
+The source of the coordinate list. The options are:
+.ls grid
+Generate a list of \fInx * ny\fR coordinates evenly spaced over
+the reference image, and beginning and ending at logical coordinates
+\fIxmin\fR and \fIxmax\fR in x and \fIymin\fR and \fIymax\fR in y.
+.le
+.ls <filename>
+The name of the text file containing the world coordinates of a set of
+points in the reference image.
+.le
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum logical x and logical y coordinates used to generate
+the grid of control points if \fIcoords\fR = "grid". Xmin, xmax, ymin, and
+ymax default to 1, the number of columns in the reference image, 1, and the
+number of lines in the reference image, respectively.
+.le
+.ls nx = 10, ny = 10
+The number of points in x and y used to generate the coordinate grid
+if \fIcoords\fR = "grid".
+.le
+.ls wcs = "world"
+The world coordinate system of the coordinates. The options are:
+.ls physical
+Physical coordinates are pixel coordinates which are invariant with
+respect to linear transformations of the physical image data. For example,
+if the reference
+image is a rotated section of a larger input image, the physical
+coordinates of an object in the reference image are equal to the physical
+coordinates of the same object in the input image, although the logical
+pixel coordinates are different.
+.le
+.ls world
+World coordinates are image coordinates which are invariant with
+respect to linear transformations of the physical image data and which
+are in decimal degrees for the celestial coordinate systems. Obviously if the
+wcs is correct the ra and dec of an object
+should remain the same no matter how the image
+is linearly transformed. The default world coordinate
+system is either 1) the value of the environment variable "defwcs" if
+set in the user's IRAF environment (normally it is undefined) and present
+in the image header, 2) the value of the "system"
+attribute in the image header keyword WAT0_001 if present in the
+image header or, 3) the "physical" coordinate system.
+Care must be taken that the wcs of the input and
+reference images are compatible, e.g. it makes no sense to
+match the coordinates of a 2D sky projection and a 2D spectral wcs.
+.le
+.le
+.ls xcolumn = 1, ycolumn = 2
+The columns in the input coordinate list containing the x and y coordinate
+values if \fIcoords\fR = <filename>.
+.le
+.ls xunits = "", ls yunits = ""
+The units of the x and y coordinates in the input coordinate list
+if \fIcoords\fR = <filename>, by default decimal degrees for celestial
+coordinate systems, otherwise any units.
+The options are:
+.ls hours
+Input coordinates specified in hours are converted to decimal degrees by
+multiplying by 15.0.
+.le
+.ls native
+The internal units of the wcs. No conversions on the input coordinates
+are performed.
+.le
+
+If the units are not specified the default is "native".
+.le
+.ls xformat = "%10.3f", yformat = "%10.3f"
+The format of the output logical x and y reference and input pixel
+coordinates in columns 1 and 2 and 3 and 4 respectively. By default the
+coordinates are output right justified in a field of ten spaces with
+3 digits following the decimal point.
+.le
+.ls rwxformat = "", rwyformat = ""
+The format of the output world x and y reference image coordinates
+in columns 5 and 6 respectively. The internal default formats will give
+reasonable output formats and precision for sky projection coordinates.
+.le
+.ls wxformat = "", wyformat = ""
+The format of the output world x and y input image coordinates
+in columns 7 and 8 respectively. The internal default formats will give
+reasonable output formats and precision for sky projection coordinates.
+.le
+.ls min_sigdigits = 7
+The minimum precision of the output coordinates if, the formatting parameters
+are undefined, or the output world coordinate system is "world" and the wcs
+cannot be decoded.
+.le
+.ls verbose = yes
+Print messages about the progress of the task?
+.le
+
+.ih
+DESCRIPTION
+
+SKYXYMATCH matches the logical x and y pixel coordinates of a set of points
+in the input image \fIinput\fR with the logical x and y pixels coordinates
+of the same points in the reference image \fIreference\fR
+using world celestial coordinate information
+in the image headers. SKYXYMATCH writes its results to the
+coordinate file \fIoutput\fR which is suitable for input to the GEOMAP task.
+The input and reference images may be 1D or 2D but must both have
+the same dimensionality.
+
+If \fIcoords\fR = "grid", SKYXYMATCH computes a grid of \fInx * ny\fR
+logical x and y pixel coordinates evenly distributed over the
+logical pixel space of the reference image defined by the
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR, \fIymax\fR parameters.
+The logical x and y reference image pixel coordinates are transformed to
+reference image celestial coordinates using
+world coordinate information stored in the reference image header.
+The reference image celestial coordinates are transformed to
+input image celestial coordinates using world coordinate
+system information in both the reference and the input image headers.
+Finally the input image celestial coordinates are transformed to logical x and y
+input image pixel coordinates using world coordinate system information
+stored in the input image header. The transformation sequence looks
+like the following for an equatorial celestial coordinate system:
+
+.nf
+ (x,y) reference -> (ra,dec) reference (reference image wcs)
+(ra,dec) reference -> (ra,dec) input (reference and input image wcs)
+ (ra,dec) input -> (x,y) input (input image wcs)
+.fi
+
+The reference and input image celestial coordinate systems
+may be equatorial, ecliptic, galactic, or supergalactic. The equatorial systems
+may be one of: 1) the mean place pre-IAU 1976 (FK4) system, 2)
+the same as FK4 but without the E-terms (FK4-NO-E) system, 3) the mean
+place post-IAU
+1976 (FK5) system, 4) or the geocentric apparent place in the post-IAU 1976
+(GAPPT) system.
+
+SKYXYMATCH assumes that the celestial coordinate system is specified by the FITS
+keywords CTYPE, CRPIX, CRVAL, CD (or alternatively CDELT / CROTA), RADECSYS,
+EQUINOX (or EPOCH), MJD-WCS (or MJD-OBS, or DATE-OBS). USERS SHOULD TAKE NOTE
+THAT MJD-WCS IS CURRENTLY NEITHER A STANDARD OR A PROPOSED STANDARD FITS
+KEYWORD. HOWEVER IT OR SOMETHING SIMILAR, IS REQUIRED TO SPECIFY THE EPOCH OF
+THE COORDINATE SYSTEM WHICH MAY BE DIFFERENT FROM THE EPOCH OF THE OBSERVATION.
+
+The first four characters of the values of the ra / longitude and dec / latitude
+axis CTYPE keywords specify the celestial coordinate system. The currently
+permitted values of CTYPE[1:4] are RA-- / DEC- for equatorial coordinate
+systems, ELON / ELAT for the ecliptic coordinate system, GLON / GLAT for the
+galactic coordinate system, and SLON / SLAT for the supergalactic coordinate
+system.
+
+The second four characters of the values of the ra / longitude and dec /
+latitude axis CTYPE keywords specify the sky projection geometry. IRAF
+currently supports the TAN, SIN, ARC, and GLS geometries, and consequently the
+currently permitted values of CTYPE[5:8] are -TAN, -ARC, -SIN, and -GLS.
+SKYXYMATCH fully supports the TAN, SIN, and ARC projections, but does not fully
+support the GLS projection.
+
+If the image celestial coordinate systems are equatorial, the value of the
+RADECSYS keyword specifies which fundamental equatorial system is to be
+considered. The permitted values of RADECSYS are FK4, FK4-NO-E, FK5, and GAPPT.
+If the RADECSYS keyword is not present in the image header, the values of the
+EQUINOX / EPOCH keywords (in that order of precedence) are used to determine
+the fundamental equatorial coordinate system. EQUINOX or EPOCH contain the
+epoch of the mean place and equinox for the FK4, FK4-NO-E, and FK5 systems
+(e.g 1950.0 or 2000.0). The default equatorial system is FK4 if EQUINOX or
+EPOCH < 1984.0, FK5 if EQUINOX or EPOCH >= 1984.0, and FK5 if RADECSYS, EQUINOX,
+and EPOCH are undefined. If RADECSYS is defined but EQUINOX and EPOCH are not,
+the equinox defaults to 1950.0 for the FK4 and FK4-NO-E systems, and 2000.0 for
+the FK5 system. The equinox value is interpreted as a Besselian epoch for the
+FK4 and FK4-NO-E systems, and as a Julian epoch for the FK5 system. Users are
+strongly urged to use the EQUINOX keyword in preference to the EPOCH keyword,
+if they must enter their own equinox values into the image header. The FK4 and
+FK4-NO-E systems are not inertial and therefore also require the epoch of the
+observation (the time when the mean place was correct), in addition to the
+equinox. The epoch is specified, in order of precedence, by the values of the
+keywords MJD-WCS or MJD-OBS (which contain the modified Julian date, JD -
+2400000.5, of the coordinate system), or the DATE-OBS keyword (which contains
+the date of the observation in the form DD/MM/YY, CCYY-MM-DD,
+CCYY-MM-DDTHH:MM:SS.S). As the latter quantity is
+only accurate to a day, the MJD-WCS or MJD-OBS specification is preferred.
+If all 3 keywords are absent the epoch defaults to the value of equinox.
+Equatorial coordinates in the GAPPT system require only the specification
+of the epoch of observation which is supplied via the MJD-WCS, MJD-OBS,
+or DATE-OBS keywords (in that order of precedence) as for the FK4 and
+FK4-NO-E system.
+
+If the image celestial coordinate systems are ecliptic the mean ecliptic
+and equinox of date are required. These are read from the MJD-WCS, MJD-OBS,
+or DATE-OBS keywords (in that order or precedence) as for the equatorial FK4,
+FK4-NO-E, and GAPPT systems.
+
+USERS NEED TO BE AWARE THAT THE IRAF IMAGE WORLD COORDINATE SYSTEM
+CURRENTLY (IRAF VERSIONS 2.10.4 PATCH 2 AND EARLIER) SUPPORTS ONLY THE
+EQUATORIAL SYSTEM (CTYPE<lngax> = "RA--XXXX" CTYPE<latax> = "DEC-XXXX")
+WHERE XXXX IS THE PROJECTION TYPE, EVEN THOUGH THE SKYXYMATCH TASK
+SUPPORTS GALACTIC, SUPERGALACTIC, AND ECLIPTIC coordinate systems.
+
+If \fIcoords\fR is a file name, SKYXYMATCH reads a list of x and y
+reference image world coordinates from columns \fIxcolumn\fR and \fIycolumn\fR
+in the input coordinates file and transforms these coordinates to
+"native" coordinate units using the \fIxunits\fR and \fIyunits\fR parameters.
+The reference image world coordinates are
+transformed to logical reference and input image coordinates
+using the value of the \fIwcs\fR parameter and world coordinate
+information in the reference and input image headers.
+
+SKYXYMATCH will terminate with an error if the reference and input images
+are not both either 1D or 2D.
+If the world coordinate system information cannot be read from either
+the reference or input image header, the requested transformations
+from the world <-> logical coordinate systems cannot be compiled for either
+or both images, or the world coordinate systems of the reference and input
+images are fundamentally incompatible in some way, the output logical
+reference and input image coordinates are both set to a grid of points
+spanning the logical pixel space of the input, not the reference image,
+and defining an identify transformation, is written to the output file.
+
+The computed reference and input logical and world coordinates
+are written to the output file using
+the \fIxformat\fR and \fIyformat\fR, \fIrwxformat\fr, \fIrwyformat\fR,
+and the \fIwxformat\fR and \fIwxformat\fR
+parameters respectively. If these formats are undefined and, in the
+case of the world coordinates, a format attribute cannot be
+read from either the reference or the input images reasonable defaults are
+chosen.
+
+If the reference and input images are 1D then the
+output logical and world y coordinates are
+set to 1.
+
+If \fIverbose\fR is "yes" then a title section is written to the output
+file for each set of computed coordinates, along with messages about
+what if anything went wrong with the computation.
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+REFERENCES
+
+Additional information on IRAF world coordinate systems including
+more detailed descriptions of the "logical", "physical", and "world"
+coordinate systems can be found in the help pages for the WCSEDIT
+and WCRESET tasks. Detailed documentation for the IRAF world
+coordinate system interface MWCS can be found in the file
+"iraf$sys/mwcs/MWCS.hlp". This file can be formatted and printed
+with the command "help iraf$sys/mwcs/MWCS.hlp fi+ | lprint".
+
+Details of the FITS header world coordinate system interface can
+be found in the draft paper "World Coordinate Systems Representations Within the
+FITS Format" by Hanisch and Wells, available from the iraf anonymous ftp
+archive and the draft paper which supersedes it "Representations of Celestial
+Coordinates in FITS" by Greisen and Calabretta available from the NRAO
+anonymous ftp archives.
+
+The spherical astronomy routines employed here are derived from the Starlink
+SLALIB library provided courtesy of Patrick Wallace. These routines
+are very well documented internally with extensive references provided
+where appropriate. Interested users are encouraged to examine the routines
+for this information. Type "help slalib" to get a listing of the SLALIB
+routines, "help slalib opt=sys" to get a concise summary of the library,
+and "help <routine>" to get a description of each routine's calling sequence,
+required input and output, etc. An overview of the library can be found in the
+paper "SLALIB - A Library of Subprograms", Starlink User Note 67.7
+by P.T. Wallace, available from the Starlink archives.
+
+.ih
+EXAMPLES
+
+1. Compute a matched list of 100 logical x and y coordinates for an X-ray
+and radio image of the same field, both of which have accurate sky
+projection world coordinate systems with different equinoxes. Print the
+output world coordinates in hh:mm:ss.ss and dd:mm:ss.s format
+
+.nf
+ cl> skyxymatch image refimage coords rwxformat=%12.2H \
+ rwyformat=%12.1h wxformat=%12.2H wyformat=%12.1h
+.fi
+
+2. Given a list of ras and decs of objects in the reference image,
+compute a list of matched logical x and y coordinates for the two images,
+both of which have a accurate sky projection wcss, although the reference
+wcs is in equatorial coordinates and the input wcs is in galactic
+coordinates. The ras and decs are in
+columns 3 and 4 of the input coordinate file and are in hh:mm:ss.ss and
+dd:mm:ss.s format respectively. Print the output world coordinates
+in the same units as the input.
+
+.nf
+ cl> skyxymatch image refimage coords coords=radecs \
+ xcolumn=3 ycolumn=4 xunits=hours rwxformat=%12.2H \
+ rwyformat=%12.1h wxformat=%12.2H wyformat=%12.1h
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+skyctran,wcsctran,geomap,geotran,skymap,sregister
+.endhelp
diff --git a/pkg/images/immatch/doc/sregister.hlp b/pkg/images/immatch/doc/sregister.hlp
new file mode 100644
index 00000000..5bc829c5
--- /dev/null
+++ b/pkg/images/immatch/doc/sregister.hlp
@@ -0,0 +1,779 @@
+.help sregister Dec98 images.immatch
+.ih
+NAME
+sregister -- register a list of images to a reference image using celestial
+coordinate WCS information
+.ih
+USAGE
+sregister input reference output
+.ih
+PARAMETERS
+.ls input
+The list of input images containing the input celestial coordinate wcs.
+.le
+.ls reference
+The list of reference images containing the reference celestial coordinate wcs.
+The number of reference images must be one or equal to the number of
+input images.
+.le
+.ls output
+The list of output registered images. The number of output images must
+be equal to the number of input images.
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum logical x and logical y coordinates used to, generate
+the grid of reference image control points, define the region of validity of
+the spatial transformation, and define the extent of the output image.
+Xmin, xmax, ymin, and
+ymax are assigned defaults of 1, the number of columns in the reference
+image, 1, and the number of lines in the reference image, respectively.
+.le
+.ls nx = 10, ny = 10
+The number of points in x and y used to generate the coordinate grid.
+.le
+.ls wcs = "world"
+The world coordinate system of the coordinates. The options are:
+.ls physical
+Physical coordinates are pixel coordinates which are invariant with
+respect to linear transformations of the physical image data. For example,
+if the reference
+image is a rotated section of a larger input image, the physical
+coordinates of an object in the reference image are equal to the physical
+coordinates of the same object in the input image, although the logical
+pixel coordinates are different.
+.le
+.ls world
+World coordinates are image coordinates which are invariant with
+respect to linear transformations of the physical image data and which
+are in decimal degrees for celestial coordinate systems. Obviously if the
+wcs is correct the ra and dec of an object
+should remain the same no matter how the image
+is linearly transformed. The default world coordinate
+system is either 1) the value of the environment variable "defwcs" if
+set in the user's IRAF environment (normally it is undefined) and present
+in the image header, 2) the value of the "system"
+attribute in the image header keyword WAT0_001 if present in the
+image header or, 3) the "physical" coordinate system.
+Care must be taken that the wcs of the input and
+reference images are compatible, e.g. it makes no sense to
+match the coordinates of a 2D sky projection and a 2D spectral wcs.
+.le
+.le
+.ls xformat = "%10.3f", yformat = "%10.3f"
+The format of the output logical x and y reference and input pixel
+coordinates in columns 1 and 2 and 3 and 4 respectively. By default the
+coordinates are output right justified in a field of ten spaces with
+3 digits following the decimal point.
+.le
+.ls rwxformat = "", rwyformat = ""
+The format of the output world x and y reference image coordinates
+in columns 5 and 6 respectively. The internal default formats will give
+reasonable output formats and precision for celestial coordinate
+systems.
+.le
+.ls wxformat = "", wyformat = ""
+The format of the output world x and y input image coordinates
+in columns 7 and 8 respectively. The internal default formats will give
+reasonable output formats and precision for celestial coordinate
+systems.
+.le
+.ls fitgeometry = "general"
+The fitting geometry to be used. The options are the following.
+.ls shift
+X and y shifts only are fit.
+.le
+.ls xyscale
+X and y shifts and x and y magnification factors are fit. Axis flips are
+allowed for.
+.le
+.ls rotate
+X and y shifts and a rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rscale
+X and y shifts, a magnification factor assumed to be the same in x and y, and a
+rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rxyscale
+X and y shifts, x and y magnifications factors, and a rotation angle are fit.
+Axis flips are allowed for.
+.le
+.ls general
+A polynomial of arbitrary order in x and y is fit. A linear term and a
+distortion term are computed separately. The linear term includes an x and y
+shift, an x and y scale factor, a rotation and a skew. Axis flips are also
+allowed for in the linear portion of the fit. The distortion term consists
+of a polynomial fit to the residuals of the linear term. By default the
+distortion terms is set to zero.
+.le
+
+For all the fitting geometries except "general" no distortion term is fit,
+i.e. the x and y polynomial orders are assumed to be 2 and the cross term
+switches are set to "none", regardless of the values of the \fIxxorder\fR,
+\fIxyorder\fR, \fIxxterms\fR, \fIyxorder\fR, \fIyyorder\fR and \fIyxterms\fR
+parameters set by the user.
+.le
+.ls function = "polynomial"
+The type of analytic coordinate surfaces to be fit. The options are the
+following:
+.ls legendre
+Legendre polynomials in x and y.
+.le
+.ls chebyshev
+Chebyshev polynomials in x and y.
+.le
+.ls polynomial
+Power series polynomials in x and y.
+.le
+.le
+.ls xxorder = 2, xyorder = 2, yxorder = 2, yyorder = 2
+The order of the polynomials in x and y for the x and y fits respectively.
+The default order and cross term settings define the linear term in x
+and y, where the 6 coefficients can be interpreted in terms of an x and y shift,
+an x and y scale change, and rotations of the x and y axes. The "shift",
+"xyscale", "rotation", "rscale", and "rxyscale", fitting geometries
+assume that the polynomial order parameters are 2 regardless of the values
+set by the user. If any of the order parameters are higher than 2 and
+\fIfitgeometry\fR is "general", then a distortion surface is fit to the
+residuals from the linear portion of the fit.
+.le
+.ls xxterms = "half", yxterms = "half"
+The options are:
+.ls none
+The individual polynomial terms contain powers of x or powers of y but not
+powers of both.
+.le
+.ls half
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1, xyorder - 1) for the x fit and
+MAX (yxorder - 1, yyorder - 1) for the y fit.
+.le
+.ls full
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1 + xyorder - 1) for the x fit and
+MAX (yxorder - 1 + yyorder - 1) for the y fit.
+.le
+
+The "shift", "xyscale", "rotation", "rscale", and "rxyscale" fitting
+geometries, assume that the cross term switches are set to "none"regardless
+of the values set by the user. If either of the cross terms parameters is
+set to "half" or "full" and \fIfitgeometry\fR is "general" then a distortion
+surface is fit to the residuals from the linear portion of the fit.
+.le
+
+.ls reject = INDEF
+The rejection limit in units of sigma. The default is no rejection.
+.le
+.ls calctype = "real"
+The precision of coordinate transformation calculations. The options are "real"
+and "double".
+.le
+.ls geometry = "geometric"
+The type of geometric transformation. The options are:
+.ls linear
+Perform only the linear part of the geometric transformation.
+.le
+.ls geometric
+Compute both the linear and distortion portions of the geometric correction.
+.le
+.le
+.ls xsample = 1.0, ysample = 1.0
+The coordinate surface subsampling factor. The coordinate surfaces are
+evaluated at every xsample-th pixel in x and every ysample-th pixel in y.
+Transformed coordinates at intermediate pixel values are determined by
+bilinear interpolation in the coordinate surfaces. If the coordinate
+surface is of high order setting these numbers to some reasonably high
+value is recommended.
+.le
+.ls interpolant = "linear"
+The interpolant used for rebinning the image. The choices are the following.
+.ls nearest
+Nearest neighbor.
+.le
+.ls linear
+Bilinear interpolation in x and y.
+.le
+.ls poly3
+Third order polynomial in x and y.
+.le
+.ls poly5
+Fifth order polynomial in x and y.
+.le
+.ls spline3
+Bicubic spline.
+.le
+.ls sinc
+2D sinc interpolation. Users can specify the sinc interpolant width by
+appending a width value to the interpolant string, e.g. sinc51 specifies
+a 51 by 51 pixel wide sinc interpolant. The sinc width will be rounded up to
+the nearest odd number. The default sinc width is 31 by 31.
+.le
+.ls lsinc
+Look-up table sinc interpolation. Users can specify the look-up table sinc
+interpolant width by appending a width value to the interpolant string, e.g.
+lsinc51 specifies a 51 by 51 pixel wide look-up table sinc interpolant. The user
+supplied sinc width will be rounded up to the nearest odd number. The default
+sinc width is 31 by 31 pixels. Users can specify the resolution of the lookup
+table sinc by appending the look-up table size in square brackets to the
+interpolant string, e.g. lsinc51[20] specifies a 20 by 20 element sinc
+look-up table interpolant with a pixel resolution of 0.05 pixels in x and y.
+The default look-up table size and resolution are 20 by 20 and 0.05 pixels
+in x and y respectively.
+.le
+.ls drizzle
+2D drizzle resampling. Users can specify the drizzle pixel fraction in x and y
+by appending a value between 0.0 and 1.0 in square brackets to the
+interpolant string, e.g. drizzle[0.5]. The default value is 1.0.
+The value 0.0 is increased internally to 0.001. Drizzle resampling
+with a pixel fraction of 1.0 in x and y is equivalent to fractional pixel
+rotated block summing (fluxconserve = yes) or averaging (flux_conserve = no) if
+xmag and ymag are > 1.0.
+.le
+.le
+.ls boundary = "nearest"
+The choices are:
+.ls nearest
+Use the value of the nearest boundary pixel.
+.le
+.ls constant
+Use a user supplied constant value.
+.le
+.ls reflect
+Generate a value by reflecting about the boundary of the image.
+.le
+.ls wrap
+Generate a value by wrapping around to the opposite side of the image.
+.le
+.le
+.ls constant = 0.0
+The value of the constant for boundary extension.
+.le
+.ls fluxconserve = yes
+Preserve the total image flux? If flux conservation is turned on, the output
+pixel values are multiplied by the Jacobian of the coordinate transformation.
+.le
+.ls nxblock = 512, nyblock = 512
+If the size of the output image is less than nxblock by nyblock then
+the entire image is transformed at once. Otherwise the output image
+is computed in blocks nxblock by nyblock pixels.
+.le
+.ls wcsinherit = yes
+Inherit the wcs of the reference image?
+.le
+.ls verbose = yes
+Print messages about the progress of the task?
+.le
+.ls interactive = no
+Run the task interactively ?
+In interactive mode the user may interact with the fitting process, e.g.
+change the order of the fit, delete points, replot the data etc.
+.le
+.ls graphics = "stdgraph"
+The graphics device.
+.le
+.ls gcommands = ""
+The graphics cursor.
+.le
+
+.ih
+DESCRIPTION
+
+SREGISTER computes the spatial transformation function required to register
+the input image \fIinput\fR to the reference image \fIreference\fR,
+and writes the registered input image to the output image \fIoutput\fR.
+The input and reference images may be 1D or 2D but must have
+the same dimensionality. SREGISTER assumes that the world
+coordinate systems in the input and reference
+image headers are accurate and that both systems are compatible, e.g. both
+images have a celestial coordinate system WCS.
+
+SREGISTER computes the required spatial transformation by matching the logical
+x and y pixel coordinates of a grid of points
+in the input image with the logical x and y pixels coordinates
+of the same grid of points in the reference image,
+using world coordinate information stored in the two image headers.
+The coordinate grid consists of \fInx * ny\fR points evenly distributed
+over the logical pixel space of interest in the reference image defined by the
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR, \fIymax\fR parameters.
+The reference image celestial coordinates are transformed to
+input image celestial coordinates using world coordinate
+system information in both the reference and the input image headers.
+Finally the input image celestial coordinates are transformed to logical x and y
+input image pixel coordinates using world coordinate system information
+stored in the input image header. The transformation sequence looks
+like the following for an equatorial celestial coordinate system:
+
+.nf
+ (x,y) reference -> (ra,dec) reference (reference image wcs)
+(ra,dec) reference -> (ra,dec) input (reference and input image wcs)
+ (ra,dec) input -> (x,y) input (input image wcs)
+.fi
+
+The computed reference and input logical coordinates and the
+celestial coordinates are written to a temporary output coordinates file
+which is deleted on task termination. The pixel and celestial coordinates
+are output using the \fIxformat\fR and \fIyformat\fR and the \fIrwxformat\fR,
+\fIrwyformat\fR, \fIwxformat\fR and \fIwxformat\fR
+parameters respectively. If these formats are undefined and, in the
+case of the celestial coordinates a format attribute cannot be
+read from either the reference or the input images, the coordinates are
+output in %g format with \fImin_sigdigits\fR digits of precision.
+If the reference and input images are 1D then all the output logical and
+world y coordinates are set to 1.
+
+SREGISTER computes a spatial transformation of the following form.
+
+.nf
+ xin = f (xref, yref)
+ yin = g (xref, yref)
+.fi
+
+The functions f and g are either a power series polynomial or a Legendre or
+Chebyshev polynomial surface of order
+\fIxxorder\fR and \fIxyorder\fR in x and \fIyxorder\fR and \fIyyorder\fR in y.
+
+Several polynomial cross terms options are available. Options "none",
+"half", and "full" are illustrated below for a quadratic polynomial in
+x and y.
+
+.nf
+xxterms = "none", xyterms = "none"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a13' * yref ** 2
+
+xxterms = "half", xyterms = "half"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a22 * xref * yref + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a22' * xref * yref + a13' * yref ** 2
+
+xxterms = "full", xyterms = "full"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a31 * xref ** 2 +
+ a12 * yref + a22 * xref * yref + a32 * xref ** 2 * yref +
+ a13 * yref ** 2 + a23 * xref * yref ** 2 +
+ a33 * xref ** 2 * yref ** 2
+ yin = a11' + a21' * xref + a31' * xref ** 2 +
+ a12' * yref + a22' * xref * yref + a32' * xref ** 2 * yref +
+ a13' * yref ** 2 + a23' * xref * yref ** 2 +
+ a33' * xref ** 2 * yref ** 2
+.fi
+
+
+The computation can be done in either real or
+double precision by setting the \fIcalctype\fR parameter.
+Automatic pixel rejection may be enabled by setting the \fIreject\fR
+parameter to some number > 0.0.
+
+The transformation computed by the "general" fitting geometry is arbitrary
+and does not correspond to a physically meaningful model. However the computed
+coefficients for the linear term can be given a simple geometrical geometric
+interpretation for all the fitting geometries as shown below.
+
+.nf
+ fitting geometry = general (linear term)
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+
+ fitting geometry = shift
+ xin = a + xref
+ yin = d + yref
+
+ fitting geometry = xyscale
+ xin = a + b * xref
+ yin = d + f * yref
+
+ fitting geometry = rotate
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/-1
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rxyscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+.fi
+
+The coefficients can be interpreted as follows. Xref0, yref0, xin0, yin0
+are the origins in the reference and input frames respectively. Orientation
+and skew are the orientation of the x and y axes and their deviation from
+perpendicularity respectively. Xmag and ymag are the scaling factors in x and
+y and are assumed to be positive.
+
+.nf
+ general (linear term)
+ xrotation = rotation - skew / 2
+ yrotation = rotation + skew / 2
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ shift
+ xrotation = 0.0, yrotation = 0.0
+ xmag = ymag = 1.0
+ b = 1.0
+ c = 0.0
+ e = 0.0
+ f = 1.0
+ a = xin0 - xref0 = xshift
+ d = yin0 - yref0 = yshift
+
+ xyscale
+ xrotation 0.0 / 180.0 yrotation = 0.0
+ b = + /- xmag
+ c = 0.0
+ e = 0.0
+ f = ymag
+ a = xin0 - b * xref0 = xshift
+ d = yin0 - f * yref0 = yshift
+
+ rscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ mag = xmag = ymag
+ const = mag * mag
+ b = mag * cos (xrotation)
+ c = mag * sin (yrotation)
+ e = -mag * sin (xrotation)
+ f = mag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ rxyscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ const = xmag * ymag
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+.fi
+
+
+\fIXmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR define the region of
+validity of the transformation as well as the limits of the grid
+in the reference coordinate system.
+
+Each computed transformation is written to the a temporary output text database
+file which is deleted on task termination. Otherwise the
+database file is opened in append mode and new records are written
+to the end of the existing file. If more that one record of the same
+name is written to the database file, the last record written is the
+valid record.
+
+SREGISTER will terminate with an error if the reference and input images
+are not both either 1D or 2D.
+If the world coordinate system information cannot be read from either
+the reference or input image header, the requested transformations
+from the world <-> logical coordinate systems cannot be compiled for either
+or both images, or the world coordinate systems of the reference and input
+images are fundamentally incompatible in some way, the output logical
+reference and input image coordinates are both set to a grid of points
+spanning the logical pixel space of the input, not the reference image.
+This grid of points defines an identity transformation which results in
+an output image equal to the input image.
+
+SREGISTER computes the output image by evaluating the fitted coordinate
+surfaces and interpolating in the input image at position of the transformed
+coordinates. The scale of the output image is the same as the scale of the
+reference image. The extent and size of the output image are determined
+by the \fIxmin\fR, \fIxmax\fR, \fIymin\fR, and \fIymax\fR parameters
+as shown below
+
+.nf
+ xmin <= x <= xmax
+ ymin <= x <= ymax
+ ncols = xmax - xmin + 1
+ nlines = xmax - xmin + 1
+.fi
+
+SREGISTER samples the coordinate surfaces at every \fIxsample\fR and
+\fIysample\fR pixels in x and y.
+The transformed coordinates at intermediate pixel values are
+determined by bilinear interpolation in the coordinate surface. If
+\fIxsample\fR and \fIysample\fR = 1, the coordinate
+surface is evaluated at every pixel. Use of \fIxsample\fR and \fIysample\fR
+are strongly recommended for large images and high order coordinate
+surfaces in order to reduce the time required to compute the output image.
+
+The output image gray levels are determined by interpolating in the input
+image at the positions of the transformed output pixels using the
+interpolant specified by the \fIinterpolant\fR parameter. If the
+\fIfluxconserve\fR switch is set the output pixel values are multiplied by
+the Jacobian of the transformation, which preserves the flux of the entire
+image. Out-of-bounds pixels are evaluated using the \fIboundary\fR and
+\fIconstant\fR parameters.
+
+The output image is computed in \fInxblock\fR by \fInyblock\fR pixel sections.
+If possible users should set these number to values larger than the dimensions
+of the output image in order to minimize the number of disk reads and writes
+required to compute the output image. If this is not feasible and the image
+rotation is small, users should set nxblock to be greater than the number of
+columns in the output image, and nyblock to be as large as machine memory
+will permit.
+
+If \fIwcsinherit\fR = "yes", then the output image will inherit the world
+coordinate system of the reference image.
+Otherwise if the environment variable \fInomwcs\fR is "no" the world
+coordinate
+system of the input image is modified in the output image to reflect the
+effects of the \fIlinear\fR portion of the registration operation.
+Support does not yet exist in the IRAF world coordinate system interface
+for the higher order distortion corrections that SREGISTER is capable
+of performing.
+
+If \fIverbose\fR is "yes" then messages about the progress of the task
+as well as warning messages indicating potential problems
+are written to the standard output.
+
+SREGISTER may be run interactively by setting the \fIinteractive\fR
+parameter to "yes".
+In interactive mode the user has the option of viewing the fitted
+spatial transformation, changing the
+fit parameters, deleting and undeleting points, and replotting
+the data until a satisfactory
+fit has been achieved.
+
+.ih
+CURSOR COMMANDS
+
+In interactive mode the following cursor commands are currently available.
+
+.nf
+ Interactive Keystroke Commands
+
+? Print options
+f Fit the data and graph with the current graph type (g, x, r, y, s)
+g Graph the data and the current fit
+x,r Graph the x fit residuals versus x and y respectively
+y,s Graph the y fit residuals versus x and y respectively
+d,u Delete or undelete the data point nearest the cursor
+o Overplot the next graph
+c Toggle the constant x, y plotting option
+t Plot a line of constant x, y through the nearest data point
+l Print xshift, yshift, xmag, ymag, xrotate, yrotate
+q Exit the interactive curve fitting
+.fi
+
+The parameters listed below can be changed interactively with simple colon
+commands. Typing the parameter name alone will list the current value.
+
+.nf
+ Colon Parameter Editing Commands
+
+:show List parameters
+:fitgeometry Fitting geometry (shift,xyscale,rotate,
+ rscale,rxyscale,general)
+:function [value] Fitting function (chebyshev,legendre,
+ polynomial)
+:xxorder :xyorder [value] X fitting function xorder, yorder
+:yxorder :yyorder [value] Y fitting function xorder, yorder
+:xxterms :yxterms [n/h/f] X, Y fit cross term types
+:reject [value] Rejection threshold
+.fi
+
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+REFERENCES
+
+Additional information on IRAF world coordinate systems including
+more detailed descriptions of the "logical", "physical", and "world"
+coordinate systems can be found in the help pages for the WCSEDIT
+and WCRESET tasks. Detailed documentation for the IRAF world
+coordinate system interface MWCS can be found in the file
+"iraf$sys/mwcs/MWCS.hlp". This file can be formatted and printed
+with the command "help iraf$sys/mwcs/MWCS.hlp fi+ | lprint".
+
+Details of the FITS header world coordinate system interface can
+be found in the draft paper "World Coordinate Systems Representations Within the
+FITS Format" by Hanisch and Wells, available from the iraf anonymous ftp
+archive and the draft paper which supersedes it "Representations of Celestial
+Coordinates in FITS" by Greisen and Calabretta available from the NRAO
+anonymous ftp archives.
+
+The spherical astronomy routines employed here are derived from the Starlink
+SLALIB library provided courtesy of Patrick Wallace. These routines
+are very well documented internally with extensive references provided
+where appropriate. Interested users are encouraged to examine the routines
+for this information. Type "help slalib" to get a listing of the SLALIB
+routines, "help slalib opt=sys" to get a concise summary of the library,
+and "help <routine>" to get a description of each routine's calling sequence,
+required input and output, etc. An overview of the library can be found in the
+paper "SLALIB - A Library of Subprograms", Starlink User Note 67.7
+by P.T. Wallace, available from the Starlink archives.
+
+.ih
+EXAMPLES
+
+1. Register a radio image to an X-ray image of the same field using
+a 100 point coordinate grid and a simple linear transformation. Both
+images have accurate sky projection world coordinate systems. Print the
+output world coordinates in the coords file in hh:mm:ss.ss and dd:mm:ss.s
+format. Display the input and output image and blink them.
+
+.nf
+ cl> sregister radio xray radio.tran rwxformat=%12.2H \
+ rwyformat=%12.1h wxformat=%12.2H wyformat=%12.1h
+
+ cl> display radio 1 fi+
+
+ cl> display radio.tran 2 fi+
+.fi
+
+2. Repeat the previous command but begin with a higher order fit
+and run the task in interactive mode in order to examine the fit
+residuals.
+
+.nf
+ cl> sregister radio xray radio.tran rwxformat=%12.2H \
+ rwyformat=%12.1h wxformat=%12.2H wyformat=%12.1h xxo=4 \
+ xyo=4 xxt=half yxo=4 yyo=4 yxt=half inter+
+
+ ... a plot of the fit appears
+
+ ... type x and r to examine the residuals of the x
+ surface fit versus x and y
+
+ ... type y and s to examine the residuals of the y
+ surface fit versus x and y
+
+ ... delete 2 deviant points with the d key and
+ recompute the fit with the f key
+
+ ... type q to quit, save the fit, and compute the registered
+ image
+.fi
+
+
+3. Mosaic a set of 9 images covering a ~ 1 degree field into a single image
+centered at 12:32:53.1 +43:13:03. Set the output image scale to 0.5
+arc-seconds / pixel which is close the detector scale of 0.51 arc-seconds
+per pixel. Set the orientation to be north up and east to the left.
+The 9 images all have accurate world coordinate information in their headers.
+
+.nf
+ # Create a dummy reference image big enough to cover 1 square degree
+
+ cl> mkpattern refimage ncols=7200 nlines=7200 ...
+
+ # Give the dummy reference image the desired coordinate system
+
+ cl> ccsetwcs refimage "" xref=3600.5 yref=3600.5 xmag=-0.5 \
+ ymag=0.5 lngref=12:32:53.1 latref=43:13:03 ...
+
+ # Register the images using constant boundary extension and set
+ # uservalue to some reasonable value outside the good data range.
+ # It may be possible to improve performance by increasing nxblock
+ # and nyblock.
+
+ cl> sregister @inlist refimage @outlist boundary=constant \
+ constant=<uservalue> nxblock=7200 nyblock=1024 ...
+
+ # Combine the images using imcombine
+
+ cl> imcombine @outlist mosaic lthreshold=<uservalue> ...
+
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+imalign,xregister,register,geotran,wregister
+.endhelp
diff --git a/pkg/images/immatch/doc/wcscopy.hlp b/pkg/images/immatch/doc/wcscopy.hlp
new file mode 100644
index 00000000..493f1311
--- /dev/null
+++ b/pkg/images/immatch/doc/wcscopy.hlp
@@ -0,0 +1,80 @@
+.help wcscopy Jun95 images.immatch
+.ih
+NAME
+wcscopy -- copy the wcs of a reference image to a list of images
+.ih
+USAGE
+wcscopy images refimages
+.ih
+PARAMETERS
+.ls images
+The list of input images which will inherit the wcs of the reference image.
+If the image does not exists a dataless image header is created.
+.le
+.ls reference
+The list of reference images containing the reference wcs. The number of
+reference images must be one or equal to the number of input images.
+.le
+.ls verbose = yes
+Print messages about the progress of the task?
+.le
+
+.ih
+DESCRIPTION
+
+WCSCOPY copies the world coordinate system information in the header of the
+reference image \fIreference\fR to the headers of the input images
+\fIimages\fR, replacing any existing world coordinate system information
+in the input image headers in the process. WCSCOPY assumes that the
+world coordinate system information in the header of the reference
+image is accurate and that all the input images have write permission.
+If the input image does not exist a data-less image header is created.
+The WCS is treated as an independent object and
+there is no check made on the dimensionality and sizes of the images.
+
+
+.ih
+REFERENCES
+
+Information on IRAF world coordinate systems including
+more detailed descriptions of the "logical", "physical", and "world"
+coordinate systems can be
+found in the help pages for the WCSEDIT and WCRESET tasks.
+Detailed documentation for the IRAF world coordinate system
+interface MWCS can be found in the file "iraf$sys/mwcs/MWCS.hlp".
+This file can be formatted and printed with the command "help
+iraf$sys/mwcs/MWCS.hlp fi+ | lprint". Information on the spectral
+coordinates systems and their suitability for use with WCSXYMATCH
+can be obtained by typing "help specwcs | lprint".
+Details of the FITS header
+world coordinate system interface can be found in the document
+"World Coordinate Systems Representations Within the FITS Format"
+by Hanisch and Wells, available from our anonymous ftp archive.
+
+.ih
+EXAMPLES
+
+1. Make sure that the world coordinates systems of a list of input images
+that have been registered to a reference image with the xregister task
+are identical to the world coordinate system of the reference image.
+
+.nf
+ cl> xregister @inlist refimage [200:400,200:400] shifts \
+ output=@outlist xwindow=21 ywindow=21
+ cl> wcscopy @outlist refimage
+.fi
+
+2. Create a data-less WCS image by specifying a new image.
+
+.nf
+ cl> wcscopy new dev$wpix
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+tprecess,imalign,xregister,geomap,register,geotran,wcsmap,wregister,wcsedit
+.endhelp
diff --git a/pkg/images/immatch/doc/wcsmap.hlp b/pkg/images/immatch/doc/wcsmap.hlp
new file mode 100644
index 00000000..e2a4dd01
--- /dev/null
+++ b/pkg/images/immatch/doc/wcsmap.hlp
@@ -0,0 +1,619 @@
+.help wcsmap Feb96 images.immatch
+.ih
+NAME
+wcsmap -- compute the spatial transformation function required to register
+a list of images using WCS information
+.ih
+USAGE
+wcsmap input reference database
+.ih
+PARAMETERS
+.ls input
+The list of input images containing the input wcs.
+.le
+.ls reference
+The list of reference images containing the reference wcs. The number of
+reference images must be one or equal to the number of input images.
+.le
+.ls database
+The name of the output text database file containing the computed
+transformations.
+.le
+.ls transforms = ""
+An optional list of transform names. If transforms is undefined the
+transforms are assigned record names identical to the names of the input images.
+.le
+.ls results = ""
+Optional output files containing a summary of the results including a
+description of the transform geometry and a listing of the input coordinates,
+the fitted coordinates, and the fit residuals. The number of results files
+must be one or equal to the number of input files. If results is "STDOUT" the
+results summary is printed on the standard output.
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum logical x and logical y coordinates used to generate
+the grid of reference image control points and define the region of
+validity of the spatial transformation. Xmin, xmax, ymin, and
+ymax are assigned defaults of 1, the number of columns in the reference
+image, 1, and the number of lines in the reference image, respectively.
+.le
+.ls nx = 10, ny = 10
+The number of points in x and y used to generate the coordinate grid.
+.le
+.ls wcs = "world"
+The world coordinate system of the coordinates. The options are:
+.ls physical
+Physical coordinates are pixel coordinates which are invariant with
+respect to linear transformations of the physical image data. For example,
+if the reference
+image is a rotated section of a larger input image, the physical
+coordinates of an object in the reference image are equal to the physical
+coordinates of the same object in the input image, although the logical
+pixel coordinates are different.
+.le
+.ls world
+World coordinates are image coordinates which are invariant with
+respect to linear transformations of the physical image data and which
+are in world units, normally decimal degrees for sky projection coordinate
+systems and angstroms for spectral coordinate systems. Obviously if the
+wcs is correct the ra and dec or wavelength and position of an object
+should remain the same not matter how the image
+is linearly transformed. The default world coordinate
+system is either 1) the value of the environment variable "defwcs" if
+set in the user's IRAF environment (normally it is undefined) and present
+in the image header, 2) the value of the "system"
+attribute in the image header keyword WAT0_001 if present in the
+image header or, 3) the "physical" coordinate system.
+Care must be taken that the wcs of the input and
+reference images are compatible, e.g. it makes no sense to
+match the coordinates of a 2D sky projection and a 2D spectral wcs.
+.le
+.le
+.ls transpose = no
+Force a transpose of the reference image world coordinates before evaluating
+the world to logical coordinate transformation for the input image ? This
+option is useful if there is not enough information in the reference and
+input image headers to tell whether or not the images are transposed with
+respect to each other.
+.le
+.ls xformat = "%10.3f", yformat = "%10.3f"
+The format of the output logical x and y reference and input pixel
+coordinates in columns 1 and 2 and 3 and 4 respectively. By default the
+coordinates are output right justified in a field of ten spaces with
+3 digits following the decimal point.
+.le
+.ls wxformat = "", wyformat = ""
+The format of the output world x and y reference and input image coordinates
+in columns 5 and 6 respectively. The internal default formats will give
+reasonable output formats and precision for both sky projection coordinates
+and other types, e.g. spectral, coordinates.
+.le
+.ls fitgeometry = "general"
+The fitting geometry to be used. The options are the following.
+.ls shift
+X and y shifts only are fit.
+.le
+.ls xyscale
+X and y shifts and x and y magnification factors are fit. Axis flips are
+allowed for.
+.le
+.ls rotate
+X and y shifts and a rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rscale
+X and y shifts, a magnification factor assumed to be the same in x and y, and a
+rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rxyscale
+X and y shifts, x and y magnifications factors, and a rotation angle are fit.
+Axis flips are allowed for.
+.le
+.ls general
+A polynomial of arbitrary order in x and y is fit. A linear term and a
+distortion term are computed separately. The linear term includes an x and y
+shift, an x and y scale factor, a rotation and a skew. Axis flips are also
+allowed for in the linear portion of the fit. The distortion term consists
+of a polynomial fit to the residuals of the linear term. By default the
+distortion terms is set to zero.
+.le
+
+For all the fitting geometries except "general" no distortion term is fit,
+i.e. the x and y polynomial orders are assumed to be 2 and the cross term
+switches are set to "none", regardless of the values of the \fIxxorder\fR,
+\fIxyorder\fR, \fIxxterms\fR, \fIyxorder\fR, \fIyyorder\fR and \fIyxterms\fR
+parameters set by the user.
+.le
+.ls function = "polynomial"
+The type of analytic coordinate surfaces to be fit. The options are the
+following.
+.ls legendre
+Legendre polynomials in x and y.
+.le
+.ls chebyshev
+Chebyshev polynomials in x and y.
+.le
+.ls polynomial
+Power series polynomials in x and y.
+.le
+.le
+.ls xxorder = 2, xyorder = 2, yxorder = 2, yyorder = 2
+The order of the polynomials in x and y for the x and y fits respectively.
+The default order and cross term settings define the linear term in x
+and y, where the 6 coefficients can be interpreted in terms of an x and y shift,
+an x and y scale change, and rotations of the x and y axes. The "shift",
+"xyscale", "rotation", "rscale", and "rxyscale", fitting geometries
+assume that the polynomial order parameters are 2 regardless of the values
+set by the user. If any of the order parameters are higher than 2 and
+\fIfitgeometry\fR is "general", then a distortion surface is fit to the
+residuals from the linear portion of the fit.
+.le
+.ls xxterms = "half", yxterms = "half"
+The options are:
+.ls none
+The individual polynomial terms contain powers of x or powers of y but not
+powers of both.
+.le
+.ls half
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1, xyorder - 1) for the x fit and
+MAX (yxorder - 1, yyorder - 1) for the y fit.
+.le
+.ls full
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1 + xyorder - 1) for the x fit and
+MAX (yxorder - 1 + yyorder - 1) for the y fit.
+.le
+
+The "shift", "xyscale", "rotation", "rscale", and "rxyscale" fitting
+geometries, assume that the cross term switches are set to "none"regardless
+of the values set by the user. If either of the cross terms parameters is
+set to "half" or "full" and \fIfitgeometry\fR is "general" then a distortion
+surface is fit to the residuals from the linear portion of the fit.
+.le
+.ls reject = INDEF
+The rejection limit in units of sigma. The default is no rejection.
+.le
+.ls calctype = "real"
+The precision of coordinate transformation calculations. The options are "real"
+and "double".
+.le
+.ls verbose = yes
+Print messages about the progress of the task?
+.le
+.ls interactive = yes
+Run the task interactively ?
+In interactive mode the user may interact with the fitting process, e.g.
+change the order of the fit, delete points, replot the data etc.
+.le
+.ls graphics = "stdgraph"
+The graphics device.
+.le
+.ls gcommands = ""
+The graphics cursor.
+.le
+
+.ih
+DESCRIPTION
+
+WCSMAP computes the spatial transformation function required to map the
+coordinate system of the reference image \fIreference\fR to the coordinate
+system of the input image \fIinput\fR, and stores the computed function in
+the output text database file \fIdatabase\fR.
+The input and reference images must be one- or two-dimensional and
+must have the same dimensionality. The input image and output
+text database file can be input to the REGISTER or GEOTRAN tasks to
+perform the actual image registration. WCSMAP assumes that the world
+coordinate systems in the input and reference
+image headers are accurate and that the two systems are compatible, e.g. both
+images have the same epoch sky projection world coordinate systems or both are
+spectra whose coordinates are in the same units.
+
+WCSMAP computes the required spatial transformation by matching the logical
+x and y pixel coordinates of a grid of points
+in the input image with the logical x and y pixels coordinates
+of the same grid of points in the reference image,
+using world coordinate information stored in the two image headers.
+The coordinate grid consists of \fInx * ny\fR points evenly distributed
+over the logical pixel space of interest in the reference image defined by the
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR, \fIymax\fR parameters.
+The logical x and y pixel reference image coordinates are transformed to the
+reference image world coordinate system defined by \fIwcs\fR, using the wcs
+information in the reference image header.
+The reference image world coordinates are then transformed to logical x and
+y pixel coordinates in the input image, using world coordinate system
+information stored in the input image header.
+
+The computed reference and input logical coordinates and the
+world coordinates are written to a temporary output coordinates file which
+is deleted on task termination.
+The x and y coordinates are written using
+the \fIxformat\fR and \fIyformat\fR and the \fIwxformat\fR and \fIwxformat\fR
+parameters respectively. If these formats are undefined and, in the
+case of the world coordinates a format attribute cannot be
+read from either the reference or the input images, the coordinates are
+output in %g format with \fImin_sigdigits\fR digits of precision.
+If the reference and input images are 1D then all the output logical and
+world y coordinates are set to 1.
+
+WCSMAP computes a spatial transformation of the following form.
+
+.nf
+ xin = f (xref, yref)
+ yin = g (xref, yref)
+.fi
+
+The functions f and g are either a power series polynomial or a Legendre or
+Chebyshev polynomial surface of order \fIxxorder\fR and \fIxyorder\fR in
+x and \fIyxorder\fR and \fIyyorder\fR in y. Cross terms are optional.
+
+Several polynomial cross terms options are available. Options "none",
+"half", and "full" are illustrated below for a quadratic polynomial in
+x and y.
+
+.nf
+xxterms = "none", xyterms = "none"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a13' * yref ** 2
+
+xxterms = "half", xyterms = "half"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a22 * xref * yref + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a22' * xref * yref + a13' * yref ** 2
+
+xxterms = "full", xyterms = "full"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a31 * xref ** 2 +
+ a12 * yref + a22 * xref * yref + a32 * xref ** 2 * yref +
+ a13 * yref ** 2 + a23 * xref * yref ** 2 +
+ a33 * xref ** 2 * yref ** 2
+ yin = a11' + a21' * xref + a31' * xref ** 2 +
+ a12' * yref + a22' * xref * yref + a32' * xref ** 2 * yref +
+ a13' * yref ** 2 + a23' * xref * yref ** 2 +
+ a33' * xref ** 2 * yref ** 2
+.fi
+
+If the \fBfitgeometry\fR parameter is anything
+other than "general", the order parameters assume the value 2 and the
+cross terms switches assume the value "none", regardless of the values set
+by the user. The computation can be done in either real or
+double precision by setting the \fIcalctype\fR parameter.
+Automatic pixel rejection may be enabled by setting the \fIreject\fR
+parameter to some number > 0.0.
+
+The transformation computed by the "general" fitting geometry is arbitrary
+and does not correspond to a physically meaningful model. However the computed
+coefficients for the linear term can be given a simple geometrical geometric
+interpretation for all the fitting geometries as shown below.
+
+.nf
+ fitting geometry = general (linear term)
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+
+ fitting geometry = shift
+ xin = a + xref
+ yin = d + yref
+
+ fitting geometry = xyscale
+ xin = a + b * xref
+ yin = d + f * yref
+
+ fitting geometry = rotate
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/-1
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rxyscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+.fi
+
+
+The coefficients can be interpreted as follows. Xref0, yref0, xin0, yin0
+are the origins in the reference and input frames respectively. Orientation
+and skew are the orientation of the x and y axes and their deviation from
+perpendicularity respectively. Xmag and ymag are the scaling factors in x and
+y and are assumed to be positive.
+
+.nf
+ general (linear term)
+ xrotation = rotation - skew / 2
+ yrotation = rotation + skew / 2
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ shift
+ xrotation = 0.0, yrotation = 0.0
+ xmag = ymag = 1.0
+ b = 1.0
+ c = 0.0
+ e = 0.0
+ f = 1.0
+ a = xin0 - xref0 = xshift
+ d = yin0 - yref0 = yshift
+
+ xyscale
+ xrotation 0.0 / 180.0 yrotation = 0.0
+ b = + /- xmag
+ c = 0.0
+ e = 0.0
+ f = ymag
+ a = xin0 - b * xref0 = xshift
+ d = yin0 - f * yref0 = yshift
+
+ rscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ mag = xmag = ymag
+ const = mag * mag
+ b = mag * cos (xrotation)
+ c = mag * sin (yrotation)
+ e = -mag * sin (xrotation)
+ f = mag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ rxyscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ const = xmag * ymag
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+.fi
+
+
+\fIXmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR define the region of
+validity of the fit as well as the limits of the grid
+in the reference coordinate system and must be set by
+the user. These parameters are used to reject out of range data before the
+actual fitting is done.
+
+Each computed transformation is written to the output file \fIdatabase\fR
+in a record whose name is either specified by the user via the \fItransforms\fR
+parameter or defaults the name of the input image.
+The database file is opened in append mode and new records are written
+to the end of the existing file. If more that one record of the same
+name is written to the database file, the last record written is the
+valid record, i.e. the one that will be used by the REGISTER or
+GEOTRAN tasks.
+
+WCSMAP will terminate with an error if the reference and input images
+are not both either 1D or 2D.
+If the world coordinate system information cannot be read from either
+the reference or input image header, the requested transformations
+from the world <-> logical coordinate systems cannot be compiled for either
+or both images, or the world coordinate systems of the reference and input
+images are fundamentally incompatible in some way, the output logical
+reference and input image coordinates are both set to a grid of points
+spanning the logical pixel space of the input, not the reference image.
+This grid of points defines an identity transformation which will leave
+the input image unchanged if applied by the REGISTER or GEOTRAN tasks.
+
+If \fIverbose\fR is "yes" then messages about the progress of the task
+as well as warning messages indicating potential problems are written to
+the standard output. If \fIresults\fR is set to a file name then the input
+coordinates, the fitted coordinates, and the residuals of the fit are
+written to that file.
+
+WCSMAP may be run interactively by setting the \fIinteractive\fR
+parameter to "yes".
+In interactive mode the user has the option of viewing the fit, changing the
+fit parameters, deleting and undeleting points, and replotting
+the data until a satisfactory
+fit has been achieved.
+
+.ih
+CURSOR COMMANDS
+
+In interactive mode the following cursor commands are currently available.
+
+.nf
+ Interactive Keystroke Commands
+
+? Print options
+f Fit the data and graph with the current graph type (g, x, r, y, s)
+g Graph the data and the current fit
+x,r Graph the x fit residuals versus x and y respectively
+y,s Graph the y fit residuals versus x and y respectively
+d,u Delete or undelete the data point nearest the cursor
+o Overplot the next graph
+c Toggle the constant x, y plotting option
+t Plot a line of constant x, y through the nearest data point
+l Print xshift, yshift, xmag, ymag, xrotate, yrotate
+q Exit the interactive curve fitting
+.fi
+
+The parameters listed below can be changed interactively with simple colon
+commands. Typing the parameter name alone will list the current value.
+
+.nf
+ Colon Parameter Editing Commands
+
+:show List parameters
+:fitgeometry Fitting geometry (shift,xyscale,rotate,
+ rscale,rxyscale,general)
+:function [value] Fitting function (chebyshev,legendre,
+ polynomial)
+:xxorder :xyorder [value] X fitting function xorder, yorder
+:yxorder :yyorder [value] Y fitting function xorder, yorder
+:xxterms :yxterms [n/h/f] X, Y fit cross terms type
+:reject [value] Rejection threshold
+.fi
+
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+REFERENCES
+
+Additional information on IRAF world coordinate systems including
+more detailed descriptions of the "logical", "physical", and "world"
+coordinate systems can be
+found in the help pages for the WCSEDIT and WCRESET tasks.
+Detailed documentation for the IRAF world coordinate system
+interface MWCS can be found in the file "iraf$sys/mwcs/MWCS.hlp".
+This file can be formatted and printed with the command "help
+iraf$sys/mwcs/MWCS.hlp fi+ | lprint". Information on the spectral
+coordinates systems and their suitability for use with WCSXYMATCH
+can be obtained by typing "help specwcs | lprint".
+Details of the FITS header
+world coordinate system interface can be found in the document
+"World Coordinate Systems Representations Within the FITS Format"
+by Hanisch and Wells, available from our anonymous ftp archive.
+
+.ih
+EXAMPLES
+
+1. Compute the spatial transformation required to match a radio image to an
+X-ray image of the same field using a 100 point coordinate grid
+and a simple linear transformation. Both images have accurate sky
+projection world coordinate systems. Print the output world coordinates
+in the coords file in hh:mm:ss.ss and dd:mm:ss.s format. Run geotran
+on the results to do the actual registration.
+
+.nf
+ cl> wcsmap radio xray geodb wxformat=%12.2H wyformat=%12.1h \
+ interactive-
+
+ cl> geotran radio radio.tran geodb radio
+.fi
+
+2. Repeat the previous command but begin with a higher order fit
+and run the task in interactive mode in order to examine the fit
+residuals.
+
+.nf
+ cl> wcsmap radio xray geodb wxformat=%12.2H wyformat=%12.1h \
+ xxo=4 xyo=4 xxt=half yxo=4 yyo=4 yxt=half
+
+ ... a plot of the fit appears
+
+ ... type x and r to examine the residuals of the x
+ surface fit versus x and y
+
+ ... type y and s to examine the residuals of the y
+ surface fit versus x and y
+
+ ... delete 2 deviant points with the d key and
+ recompute the fit with the f key
+
+ ... type q to quit and save the fit
+
+ cl> geotran radio radio.tran geodb radio
+.fi
+
+3. Repeat example 1 but assign a user name to the transform.
+
+.nf
+ cl> wcsmap radio xray geodb transforms="m82" wxformat=%12.2H \
+ wyformat=%12.1h interactive-
+
+ cl> geotran radio radio.tran geodb m82
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+wcstran,xregister,wcsxymatch,geomap,register,geotran
+.endhelp
diff --git a/pkg/images/immatch/doc/wcsxymatch.hlp b/pkg/images/immatch/doc/wcsxymatch.hlp
new file mode 100644
index 00000000..0651b0c7
--- /dev/null
+++ b/pkg/images/immatch/doc/wcsxymatch.hlp
@@ -0,0 +1,314 @@
+.help wcsxymatch Jun95 images.immatch
+.ih
+NAME
+wcsxymatch -- match input and reference image x-y coordinates using the WCS
+.ih
+USAGE
+wcsxymatch input reference output
+.ih
+PARAMETERS
+.ls input
+The list of input images containing the input wcs.
+.le
+.ls reference
+The list of reference images containing the reference wcs. The number of
+reference images must be one or equal to the number of input images.
+.le
+.ls output
+The output matched coordinate lists containing:
+1) the logical x-y pixel coordinates of a point
+in the reference image in columns 1 and 2, 2) the logical x-y pixel
+coordinates of the same point in the input image in columns 3 and 4,
+3) the world coordinates of the point in the reference and input
+image in columns 5 and 6. The output coordinate list can be
+input directly to the geomap task. The number of output files must be
+equal to the number of input images or be the standard output STDOUT.
+.le
+.ls coords = "grid"
+The source of the coordinate list. The options are:
+.ls grid
+Generate a list of \fInx * ny\fR coordinates, evenly spaced over
+the reference image, and beginning and ending at logical coordinates
+\fIxmin\fR and \fIxmax\fR in x and \fIymin\fR and \fIymax\fR in y.
+.le
+.ls <filename>
+The name of the text file containing the world coordinates of a set of
+points in the reference image.
+.le
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum logical x and logical y coordinates used to generate
+the grid of control points if \fIcoords\fR = "grid". Xmin, xmax, ymin, and
+ymax default to 1, the number of columns in the reference image, 1, and the
+number of lines in the reference image, respectively.
+.le
+.ls nx = 10, ny = 10
+The number of points in x and y used to generate the coordinate grid
+if \fIcoords\fR = "grid".
+.le
+.ls wcs = "world"
+The world coordinate system of the coordinates. The options are:
+.ls physical
+Physical coordinates are pixel coordinates which are invariant with
+respect to linear transformations of the physical image data. For example,
+if the reference
+image is a rotated section of a larger input image, the physical
+coordinates of an object in the reference image are equal to the physical
+coordinates of the same object in the input image, although the logical
+pixel coordinates are different.
+.le
+.ls world
+World coordinates are image coordinates which are invariant with
+respect to linear transformations of the physical image data and which
+are in world units, normally decimal degrees for sky projection coordinate
+systems and angstroms for spectral coordinate systems. Obviously if the
+wcs is correct the ra and dec or wavelength and position of an object
+should remain the same not matter how the image
+is linearly transformed. The default world coordinate
+system is either 1) the value of the environment variable "defwcs" if
+set in the user's IRAF environment (normally it is undefined) and present
+in the image header, 2) the value of the "system"
+attribute in the image header keyword WAT0_001 if present in the
+image header or, 3) the "physical" coordinate system.
+Care must be taken that the wcs of the input and
+reference images are compatible, e.g. it makes no sense to
+match the coordinates of a 2D sky projection and a 2D spectral wcs.
+.le
+.le
+.ls transpose = no
+Force a transpose of the reference image world coordinates before evaluating
+the world to logical coordinate transformation for the input image ? This
+option is useful if there is not enough information in the reference and
+input image headers to tell whether or not the images are transposed with
+respect to each other.
+.le
+.ls xcolumn = 1, ycolumn = 2
+The columns in the input coordinate list containing the x and y coordinate
+values if \fIcoords\fR = <filename>.
+.le
+.ls xunits = "", ls yunits = ""
+The units of the x and y coordinates in the input coordinate list
+if \fIcoords\fR = <filename>, by default decimal degrees for sky projection
+coordinate systems, otherwise any units.
+The options are:
+.ls hours
+Input coordinates specified in hours are converted to decimal degrees by
+multiplying by 15.0.
+.le
+.ls native
+The internal units of the wcs. No conversions on the input coordinates
+are performed.
+.le
+
+If the units are not specified the default is "native".
+.le
+.ls xformat = "%10.3f", yformat = "%10.3f"
+The format of the output logical x and y reference and input pixel
+coordinates in columns 1 and 2 and 3 and 4 respectively. By default the
+coordinates are output right justified in a field of ten spaces with
+3 digits following the decimal point.
+.le
+.ls wxformat = "", wyformat = ""
+The format of the output world x and y reference and input image coordinates
+in columns 5 and 6 respectively. The internal default formats will give
+reasonable output formats and precision for both sky projection coordinates
+and other types, e.g. spectral coordinates.
+.le
+.ls min_sigdigits = 7
+The minimum precision of the output coordinates if, the formatting parameters
+are undefined, or the output world coordinate system is "world" and the wcs
+format attribute is undefined.
+.le
+.ls verbose = yes
+Print messages about the progress of the task.
+.le
+
+.ih
+DESCRIPTION
+
+WCSXYMATCH matches the logical x and y pixel coordinates of a set of points
+in the input image \fIinput\fR with the logical x and y pixels coordinates
+of the same points in the reference image \fIreference\fR
+using world coordinate information
+in the respective image headers, and writes the results to a coordinate file
+\fIoutput\fR suitable for input to the GEOMAP task.
+The input and reference images may be 1D or 2D but must both have
+the same dimensionality.
+
+If \fIcoords\fR = "grid", WCSXYMATCH computes a grid of \fInx * ny\fR
+logical x and y pixel coordinates evenly distributed over the
+logical pixel space of the reference image as defined by the
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR, \fIymax\fR parameters.
+The logical x and y pixel reference image coordinates are transformed to the
+world coordinate system defined by \fIwcs\fR using
+world coordinate information stored in the reference image header.
+The world coordinates are then transformed back to the logical x and y pixel
+input image coordinates, using world coordinate system information stored in
+the input image header.
+
+If \fIcoords\fR is a file name, WCSXYMATCH reads a list of x and y
+reference image world coordinates from columns \fIxcolumn\fR and \fIycolumn\fR
+in the input coordinates file, and transforms these coordinates to
+"native" coordinate units using the \fIxunits\fR and \fIyunits\fR parameters.
+The reference image world coordinates are
+transformed to logical reference and input image coordinates
+using the value of the \fIwcs\fR parameter and world coordinate
+information in the reference and input image headers.
+
+WCSXYMATCH will terminate with an error if the reference and input images
+are not both either 1D or 2D.
+If the world coordinate system information cannot be read from either
+the reference or input image header, the requested transformations
+from the world <-> logical coordinate systems cannot be compiled for either
+or both images, or the world coordinate systems of the reference and input
+images are fundamentally incompatible in some way, the output logical
+reference and input image coordinates are both set to a grid of points
+spanning the logical pixel space of the input, not the reference image,
+and defining an identify transformation, is written to the output file.
+
+The computed reference and input logical coordinates and the
+world coordinates are written to the output file using
+the \fIxformat\fR and \fIyformat\fR, and the \fIwxformat\fR and \fIwxformat\fR
+parameters respectively. If these formats are undefined and, in the
+case of the world coordinates, a format attribute cannot be
+read from either the reference or the input images, the coordinates are
+output with the %g format and \fImin_sigdigits\fR of precision.
+
+If the reference and input images are 1D then the
+output logical and world y coordinates are
+set to 1.
+
+If \fIverbose\fR is "yes" then a title section is written to the output
+file for each set of computed coordinates, along with messages about
+what if anything went wrong with the computation.
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+REFERENCES
+
+Additional information on IRAF world coordinate systems including
+more detailed descriptions of the "logical", "physical", and "world"
+coordinate systems can be
+found in the help pages for the WCSEDIT and WCRESET tasks.
+Detailed documentation for the IRAF world coordinate system
+interface MWCS can be found in the file "iraf$sys/mwcs/MWCS.hlp".
+This file can be formatted and printed with the command "help
+iraf$sys/mwcs/MWCS.hlp fi+ | lprint". Information on the spectral
+coordinates systems and their suitability for use with WCSXYMATCH
+can be obtained by typing "help specwcs | lprint".
+Details of the FITS header
+world coordinate system interface can be found in the document
+"World Coordinate Systems Representations Within the FITS Format"
+by Hanisch and Wells, available from our anonymous ftp archive.
+
+.ih
+EXAMPLES
+
+1. Compute a matched list of 100 logical x and y coordinates for an X-ray
+and radio image of the same field, both of which have accurate sky
+projection world coordinate systems. Print the output world coordinates
+in hh:mm:ss.ss and dd:mm:ss.s format
+
+.nf
+ cl> wcsxymatch image refimage coords wxformat=%12.2H \
+ wyformat=%12.1h
+.fi
+
+2. Given a list of ras and decs of objects in the reference image,
+compute a list of matched logical x and y coordinates for the two images,
+both of which have a accurate sky projection wcss. The ras and decs are in
+columns 3 and 4 of the input coordinate file and are in hh:mm:ss.ss and
+dd:mm:ss.s format respectively. Print the output world coordinates
+in the same units as the input.
+
+.nf
+ cl> wcsxymatch image refimage coords coords=radecs \
+ xcolumn=3 ycolumn=4 xunits=hours wxformat=%12.2H \
+ wyformat=%12.1h
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+tprecess,wcstran,geomap,register,geotran,wcsmap,wregister
+.endhelp
diff --git a/pkg/images/immatch/doc/wregister.hlp b/pkg/images/immatch/doc/wregister.hlp
new file mode 100644
index 00000000..e8519803
--- /dev/null
+++ b/pkg/images/immatch/doc/wregister.hlp
@@ -0,0 +1,761 @@
+.help wregister Dec98 images.immatch
+.ih
+NAME
+wregister -- register a list of images to a reference image using WCS
+information
+.ih
+USAGE
+wregister input reference output
+.ih
+PARAMETERS
+.ls input
+The list of input images containing the input wcs.
+.le
+.ls reference
+The list of reference images containing the reference wcs. The number of
+reference images must be one or equal to the number of input images.
+.le
+.ls output
+The list of output registered images. The number of output images must
+be equal to the number of input images.
+.le
+.ls xmin = INDEF, xmax = INDEF, ymin = INDEF, ymax = INDEF
+The minimum and maximum logical x and logical y coordinates used to, generate
+the grid of reference image control points, define the region of validity of
+the spatial transformation, and define the extent of the output image.
+Xmin, xmax, ymin, and
+ymax are assigned defaults of 1, the number of columns in the reference
+image, 1, and the number of lines in the reference image, respectively.
+.le
+.ls nx = 10, ny = 10
+The number of points in x and y used to generate the coordinate grid.
+.le
+.ls wcs = "world"
+The world coordinate system of the coordinates. The options are:
+.ls physical
+Physical coordinates are pixel coordinates which are invariant with
+respect to linear transformations of the physical image data. For example,
+if the reference
+image is a rotated section of a larger input image, the physical
+coordinates of an object in the reference image are equal to the physical
+coordinates of the same object in the input image, although the logical
+pixel coordinates are different.
+.le
+.ls world
+World coordinates are image coordinates which are invariant with
+respect to linear transformations of the physical image data and which
+are in world units, normally decimal degrees for sky projection coordinate
+systems and angstroms for spectral coordinate systems. Obviously if the
+wcs is correct the ra and dec or wavelength and position of an object
+should remain the same not matter how the image
+is linearly transformed. The default world coordinate
+system is either 1) the value of the environment variable "defwcs" if
+set in the user's IRAF environment (normally it is undefined) and present
+in the image header, 2) the value of the "system"
+attribute in the image header keyword WAT0_001 if present in the
+image header or, 3) the "physical" coordinate system.
+Care must be taken that the wcs of the input and
+reference images are compatible, e.g. it makes no sense to
+match the coordinates of a 2D sky projection and a 2D spectral wcs.
+.le
+.le
+.ls transpose = no
+Force a transpose of the reference image world coordinates before evaluating
+the world to logical coordinate transformation for the input image ? This
+option is useful if there is not enough information in the reference and
+input image headers to tell whether or not the images are transposed with
+respect to each other.
+.le
+.ls xformat = "%10.3f", yformat = "%10.3f"
+The format of the output logical x and y reference and input pixel
+coordinates in columns 1 and 2 and 3 and 4 respectively. By default the
+coordinates are output right justified in a field of ten spaces with
+3 digits following the decimal point.
+.le
+.ls wxformat = "", wyformat = ""
+The format of the output world x and y reference and input image coordinates
+in columns 5 and 6 respectively. The internal default formats will give
+reasonable output formats and precision for both sky projection coordinates
+and other, e.g. spectral, coordinates.
+.le
+.ls fitgeometry = "general"
+The fitting geometry to be used. The options are the following.
+.ls shift
+X and y shifts only are fit.
+.le
+.ls xyscale
+X and y shifts and x and y magnification factors are fit. Axis flips are
+allowed for.
+.le
+.ls rotate
+X and y shifts and a rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rscale
+X and y shifts, a magnification factor assumed to be the same in x and y, and a
+rotation angle are fit. Axis flips are allowed for.
+.le
+.ls rxyscale
+X and y shifts, x and y magnifications factors, and a rotation angle are fit.
+Axis flips are allowed for.
+.le
+.ls general
+A polynomial of arbitrary order in x and y is fit. A linear term and a
+distortion term are computed separately. The linear term includes an x and y
+shift, an x and y scale factor, a rotation and a skew. Axis flips are also
+allowed for in the linear portion of the fit. The distortion term consists
+of a polynomial fit to the residuals of the linear term. By default the
+distortion terms is set to zero.
+.le
+
+For all the fitting geometries except "general" no distortion term is fit,
+i.e. the x and y polynomial orders are assumed to be 2 and the cross term
+switches are set to "none", regardless of the values of the \fIxxorder\fR,
+\fIxyorder\fR, \fIxxterms\fR, \fIyxorder\fR, \fIyyorder\fR and \fIyxterms\fR
+parameters set by the user.
+.le
+.ls function = "polynomial"
+The type of analytic coordinate surfaces to be fit. The options are the
+following:
+.ls legendre
+Legendre polynomials in x and y.
+.le
+.ls chebyshev
+Chebyshev polynomials in x and y.
+.le
+.ls polynomial
+Power series polynomials in x and y.
+.le
+.le
+.ls xxorder = 2, xyorder = 2, yxorder = 2, yyorder = 2
+The order of the polynomials in x and y for the x and y fits respectively.
+The default order and cross term settings define the linear term in x
+and y, where the 6 coefficients can be interpreted in terms of an x and y shift,
+an x and y scale change, and rotations of the x and y axes. The "shift",
+"xyscale", "rotation", "rscale", and "rxyscale", fitting geometries
+assume that the polynomial order parameters are 2 regardless of the values
+set by the user. If any of the order parameters are higher than 2 and
+\fIfitgeometry\fR is "general", then a distortion surface is fit to the
+residuals from the linear portion of the fit.
+.le
+.ls xxterms = "half", yxterms = "half"
+The options are:
+.ls none
+The individual polynomial terms contain powers of x or powers of y but not
+powers of both.
+.le
+.ls half
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1, xyorder - 1) for the x fit and
+MAX (yxorder - 1, yyorder - 1) for the y fit.
+.le
+.ls full
+The individual polynomial terms contain powers of x and powers of y, whose
+maximum combined power is MAX (xxorder - 1 + xyorder - 1) for the x fit and
+MAX (yxorder - 1 + yyorder - 1) for the y fit.
+.le
+
+The "shift", "xyscale", "rotation", "rscale", and "rxyscale" fitting
+geometries, assume that the cross term switches are set to "none"regardless
+of the values set by the user. If either of the cross terms parameters is
+set to "half" or "full" and \fIfitgeometry\fR is "general" then a distortion
+surface is fit to the residuals from the linear portion of the fit.
+.le
+.ls reject = INDEF
+The rejection limit in units of sigma. The default is no rejection.
+.le
+.ls calctype = "real"
+The precision of coordinate transformation calculations. The options are "real"
+and "double".
+.le
+.ls geometry = "geometric"
+The type of geometric transformation. The options are:
+.ls linear
+Perform only the linear part of the geometric transformation.
+.le
+.ls geometric
+Compute both the linear and distortion portions of the geometric correction.
+.le
+.le
+.ls xsample = 1.0, ysample = 1.0
+The coordinate surface subsampling factor. The coordinate surfaces are
+evaluated at every xsample-th pixel in x and every ysample-th pixel in y.
+Transformed coordinates at intermediate pixel values are determined by
+bilinear interpolation in the coordinate surfaces. If the coordinate
+surface is of high order setting these numbers to some reasonably high
+value is recommended.
+.le
+.ls interpolant = "linear"
+The interpolant used for rebinning the image. The choices are the following.
+.ls nearest
+Nearest neighbor.
+.le
+.ls linear
+Bilinear interpolation in x and y.
+.le
+.ls poly3
+Third order polynomial in x and y.
+.le
+.ls poly5
+Fifth order polynomial in x and y.
+.le
+.ls spline3
+Bicubic spline.
+.le
+.ls sinc
+2D sinc interpolation. Users can specify the sinc interpolant width by
+appending a width value to the interpolant string, e.g. sinc51 specifies
+a 51 by 51 pixel wide sinc interpolant. The sinc width will be rounded up to
+the nearest odd number. The default sinc width is 31 by 31.
+.le
+.ls lsinc
+Look-up table sinc interpolation. Users can specify the look-up table sinc
+interpolant width by appending a width value to the interpolant string, e.g.
+lsinc51 specifies a 51 by 51 pixel wide look-up table sinc interpolant. The user
+supplied sinc width will be rounded up to the nearest odd number. The default
+sinc width is 31 by 31 pixels. Users can specify the resolution of the lookup
+table sinc by appending the look-up table size in square brackets to the
+interpolant string, e.g. lsinc51[20] specifies a 20 by 20 element sinc
+look-up table interpolant with a pixel resolution of 0.05 pixels in x and y.
+The default look-up table size and resolution are 20 by 20 and 0.05 pixels
+in x and y respectively.
+.le
+.ls drizzle
+2D drizzle resampling. Users can specify the drizzle pixel fraction in x and y
+by appending a value between 0.0 and 1.0 in square brackets to the
+interpolant string, e.g. drizzle[0.5]. The default value is 1.0.
+The value 0.0 is increased internally to 0.001. Drizzle resampling
+with a pixel fraction of 1.0 in x and y is equivalent to fractional pixel
+rotated block summing (fluxconserve = yes) or averaging (flux_conserve = no) if
+xmag and ymag are > 1.0.
+.le
+.le
+.ls boundary = "nearest"
+The choices are:
+.ls nearest
+Use the value of the nearest boundary pixel.
+.le
+.ls constant
+Use a user supplied constant value.
+.le
+.ls reflect
+Generate a value by reflecting about the boundary of the image.
+.le
+.ls wrap
+Generate a value by wrapping around to the opposite side of the image.
+.le
+.le
+.ls constant = 0.0
+The value of the constant for boundary extension.
+.le
+.ls fluxconserve = yes
+Preserve the total image flux? If flux conservation is turned on, the output
+pixel values are multiplied by the Jacobian of the coordinate transformation.
+.le
+.ls nxblock = 512, nyblock = 512
+If the size of the output image is less than nxblock by nyblock then the
+entire image is computed in one iteration. Otherwise the output image is
+computed in blocks of nxblock by nyblock pixels.
+.le
+.ls wcsinherit = yes
+Inherit the wcs of the reference image ?
+.le
+.ls verbose = yes
+Print messages about the progress of the task?
+.le
+.ls interactive = no
+Run the task interactively ?
+In interactive mode the user may interact with the fitting process, e.g.
+change the order of the fit, delete points, replot the data etc.
+.le
+.ls graphics = "stdgraph"
+The graphics device.
+.le
+.ls gcommands = ""
+The graphics cursor.
+.le
+
+.ih
+DESCRIPTION
+
+WREGISTER computes the spatial transformation function required to register
+the input image \fIinput\fR to the reference image \fIreference\fR,
+and writes the registered input image to the output image \fIoutput\fR.
+The input and reference images must be one- or two-dimensional and
+have the same dimensionality. WREGISTER assumes that the world
+coordinate systems in the input and reference
+image headers are accurate and that the two systems are compatible, e.g. both
+images have the same epoch sky projection world coordinate systems, or both are
+spectra whose coordinates are in the same units.
+
+WREGISTER computes the required spatial transformation by matching the logical
+x and y pixel coordinates of a grid of points
+in the input image with the logical x and y pixels coordinates
+of the same grid of points in the reference image,
+using world coordinate information stored in the two image headers.
+The coordinate grid consists of \fInx * ny\fR points evenly distributed
+over the logical pixel space of interest in the reference image defined by the
+\fIxmin\fR, \fIxmax\fR, \fIymin\fR, \fIymax\fR parameters.
+The logical x and y pixel reference image coordinates are transformed to the
+reference image world coordinate system defined by \fIwcs\fR, using the wcs
+information in the reference image header.
+The reference image world coordinates are then transformed to logical x and
+y pixel coordinates in the input image, using world coordinate system
+information stored in the input image header.
+
+The computed reference and input logical coordinates and the
+world coordinates are written to a temporary coordinates file which is
+deleted on task termination.
+The x and y coordinates are written using
+the \fIxformat\fR and \fIyformat\fR and the \fIwxformat\fR and \fIwxformat\fR
+parameters respectively. If these formats are undefined and, in the
+case of the world coordinates a format attribute cannot be
+read from either the reference or the input images, the coordinates are
+output in %g format with \fImin_sigdigits\fR digits of precision.
+If the reference and input images are 1D then all the output logical and
+world y coordinates are set to 1.
+
+WREGISTER computes a spatial transformation of the following form.
+
+.nf
+ xin = f (xref, yref)
+ yin = g (xref, yref)
+.fi
+
+The functions f and g are either a power series polynomial or a Legendre or
+Chebyshev polynomial surface of order
+\fIxxorder\fR and \fIxyorder\fR in x and \fIyxorder\fR and \fIyyorder\fR in y.
+
+Several polynomial cross terms options are available. Options "none",
+"half", and "full" are illustrated below for a quadratic polynomial in
+x and y.
+
+.nf
+xxterms = "none", xyterms = "none"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a13' * yref ** 2
+
+xxterms = "half", xyterms = "half"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a12 * yref +
+ a31 * xref ** 2 + a22 * xref * yref + a13 * yref ** 2
+ yin = a11' + a21' * xref + a12' * yref +
+ a31' * xref ** 2 + a22' * xref * yref + a13' * yref ** 2
+
+xxterms = "full", xyterms = "full"
+xxorder = 3, xyorder = 3, yxorder = 3, yyorder = 3
+
+ xin = a11 + a21 * xref + a31 * xref ** 2 +
+ a12 * yref + a22 * xref * yref + a32 * xref ** 2 * yref +
+ a13 * yref ** 2 + a23 * xref * yref ** 2 +
+ a33 * xref ** 2 * yref ** 2
+ yin = a11' + a21' * xref + a31' * xref ** 2 +
+ a12' * yref + a22' * xref * yref + a32' * xref ** 2 * yref +
+ a13' * yref ** 2 + a23' * xref * yref ** 2 +
+ a33' * xref ** 2 * yref ** 2
+.fi
+
+
+If the \fBfitgeometry\fR parameter is anything other than "general", the order
+parameters assume the value 2 and the cross terms switches assume the value
+"none", regardless of the values set by the user. The computation can be done in
+either real or double precision by setting the \fIcalctype\fR parameter.
+Automatic pixel rejection may be enabled by setting the \fIreject\fR
+parameter to some number > 0.0.
+
+The transformation computed by the "general" fitting geometry is arbitrary
+and does not correspond to a physically meaningful model. However the computed
+coefficients for the linear term can be given a simple geometrical geometric
+interpretation for all the fitting geometries as shown below.
+
+.nf
+ fitting geometry = general (linear term)
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+
+ fitting geometry = shift
+ xin = a + xref
+ yin = d + yref
+
+ fitting geometry = xyscale
+ xin = a + b * xref
+ yin = d + f * yref
+
+ fitting geometry = rotate
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/-1
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+ b = f, c = -e or b = -f, c = e
+
+ fitting geometry = rxyscale
+ xin = a + b * xref + c * yref
+ yin = d + e * xref + f * yref
+ b * f - c * e = +/- const
+.fi
+
+
+The coefficients can be interpreted as follows. Xref0, yref0, xin0, yin0
+are the origins in the reference and input frames respectively. Orientation
+and skew are the orientation of the x and y axes and their deviation from
+perpendicularity respectively. Xmag and ymag are the scaling factors in x and
+y and are assumed to be positive.
+
+.nf
+ general (linear term)
+ xrotation = rotation - skew / 2
+ yrotation = rotation + skew / 2
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ shift
+ xrotation = 0.0, yrotation = 0.0
+ xmag = ymag = 1.0
+ b = 1.0
+ c = 0.0
+ e = 0.0
+ f = 1.0
+ a = xin0 - xref0 = xshift
+ d = yin0 - yref0 = yshift
+
+ xyscale
+ xrotation 0.0 / 180.0 yrotation = 0.0
+ b = + /- xmag
+ c = 0.0
+ e = 0.0
+ f = ymag
+ a = xin0 - b * xref0 = xshift
+ d = yin0 - f * yref0 = yshift
+
+ rscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ mag = xmag = ymag
+ const = mag * mag
+ b = mag * cos (xrotation)
+ c = mag * sin (yrotation)
+ e = -mag * sin (xrotation)
+ f = mag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+
+ rxyscale
+ xrotation = rotation + 0 / 180, yrotation = rotation
+ const = xmag * ymag
+ b = xmag * cos (xrotation)
+ c = ymag * sin (yrotation)
+ e = -xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xin0 - b * xref0 - c * yref0 = xshift
+ d = yin0 - e * xref0 - f * yref0 = yshift
+.fi
+
+
+\fIXmin\fR, \fIxmax\fR, \fIymin\fR and \fIymax\fR define the region of
+validity of the transformation as well as the limits of the grid
+in the reference coordinate system.
+
+Each computed transformation is written to a temporary output text database
+file which is deleted on task termination. If more that one record of the same
+name is written to the database file, the last record written is the
+valid record.
+
+WREGISTER will terminate with an error if the reference and input images
+are not both either 1D or 2D.
+If the world coordinate system information cannot be read from either
+the reference or input image header, the requested transformations
+from the world <-> logical coordinate systems cannot be compiled for either
+or both images, or the world coordinate systems of the reference and input
+images are fundamentally incompatible in some way, the output logical
+reference and input image coordinates are both set to a grid of points
+spanning the logical pixel space of the input, not the reference image.
+This grid of points defines an identity transformation which results in
+an output image equal to the input image.
+
+WREGISTER computes the output image by evaluating the fitted coordinate
+surfaces and interpolating in the input image at position of the transformed
+coordinates. The scale of the output image is the same as the scale of the
+reference image. The extent and size of the output image are determined
+by the \fIxmin\fR, \fIxmax\fR, \fIymin\fR, and \fIymax\fR parameters
+as shown below
+
+.nf
+ xmin <= x <= xmax
+ ymin <= x <= ymax
+ ncols = xmax - xmin + 1
+ nlines = xmax - xmin + 1
+.fi
+
+WREGISTER samples the coordinate surfaces at every \fIxsample\fR and
+fIysample\fR pixels in x and y.
+The transformed coordinates at intermediate pixel values are
+determined by bilinear interpolation in the coordinate surface. If
+\fIxsample\fR and \fIysample\fR = 1, the coordinate
+surface is evaluated at every pixel. Use of \fIxsample\fR and \fIysample\fR
+are strongly recommended for large images and high order coordinate
+surfaces in order to reduce the time required to compute the output image.
+
+The output image gray levels are determined by interpolating in the input
+image at the positions of the transformed output pixels using the
+interpolant specified by the \fIinterpolant\fR parameter. If the
+\fIfluxconserve\fR switch is set the output pixel values are multiplied by
+the Jacobian of the transformation, which preserves the flux of the entire
+image. Out-of-bounds pixels are evaluated using the \fIboundary\fR and
+\fIconstant\fR parameters.
+
+The output image is computed in \fInxblock\fR by \fInyblock\fR pixel sections.
+If possible users should set these number to values larger than the dimensions
+of the output image to minimize the number of disk reads and writes required
+to compute the output image. If this is not feasible and the image rotation is
+small users should set nxblock to be greater than the number of columns in
+the output image, and nyblock to be as large as machine memory will permit.
+
+If \fIwcsinherit\fR is "yes" then the world coordinate system of the
+reference image will be copied to the output image.
+Otherwise if the environment variable \fInomwcs\fR is "no" the
+world coordinate
+system of the input image is modified in the output image to reflect the
+effects of the \fIlinear\fR portion of the registration operation.
+Support does not yet exist in the IRAF world coordinate system interface
+for the higher order distortion corrections that WREGISTER is capable
+of performing.
+
+If \fIverbose\fR is "yes" then messages about the progress of the task
+as well as warning messages indicating potential problems
+are written to the standard output.
+
+WREGISTER may be run interactively by setting the \fIinteractive\fR
+parameter to "yes".
+In interactive mode the user has the option of viewing the fitted
+spatial transformation, changing the
+fit parameters, deleting and undeleting points, and replotting
+the data until a satisfactory
+fit has been achieved.
+
+.ih
+CURSOR COMMANDS
+
+In interactive mode the following cursor commands are currently available.
+
+.nf
+ Interactive Keystroke Commands
+
+? Print options
+f Fit the data and graph with the current graph type (g, x, r, y, s)
+g Graph the data and the current fit
+x,r Graph the x fit residuals versus x and y respectively
+y,s Graph the y fit residuals versus x and y respectively
+d,u Delete or undelete the data point nearest the cursor
+o Overplot the next graph
+c Toggle the constant x, y plotting option
+t Plot a line of constant x, y through the nearest data point
+l Print xshift, yshift, xmag, ymag, xrotate, yrotate
+q Exit the interactive curve fitting
+.fi
+
+The parameters listed below can be changed interactively with simple colon
+commands. Typing the parameter name alone will list the current value.
+
+.nf
+ Colon Parameter Editing Commands
+
+:show List parameters
+:fitgeometry Fitting geometry (shift,xyscale,rotate,
+ rscale,rxyscale,general)
+:function [value] Fitting function (chebyshev,legendre,
+ polynomial)
+:xxorder :xyorder [value] X fitting function xorder, yorder
+:yxorder :yyorder [value] Y fitting function xorder, yorder
+:xxterms :yxterms [nh/f] X, Y fit cross terms fit
+:reject [value] Rejection threshold
+.fi
+
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+REFERENCES
+
+Additional information on IRAF world coordinate systems including
+more detailed descriptions of the "logical", "physical", and "world"
+coordinate systems can be
+found in the help pages for the WCSEDIT and WCRESET tasks.
+Detailed documentation for the IRAF world coordinate system
+interface MWCS can be found in the file "iraf$sys/mwcs/MWCS.hlp".
+This file can be formatted and printed with the command "help
+iraf$sys/mwcs/MWCS.hlp fi+ | lprint". Information on the spectral
+coordinates systems and their suitability for use with WCSXYMATCH
+can be obtained by typing "help specwcs | lprint".
+Details of the FITS header
+world coordinate system interface can be found in the document
+"World Coordinate Systems Representations Within the FITS Format"
+by Hanisch and Wells, available from our anonymous ftp archive.
+
+.ih
+EXAMPLES
+
+1. Register a radio image to an X-ray image of the same field using
+a 100 point coordinate grid and a simple linear transformation. Both
+images have accurate sky projection world coordinate systems. Print the
+output world coordinates in the coords file in hh:mm:ss.ss and dd:mm:ss.s
+format. Display the input and output image and blink them.
+
+.nf
+ cl> wregister radio xray radio.tran wxformat=%12.2H \
+ wyformat=%12.1h
+
+ cl> display radio 1 fi+
+
+ cl> display radio.tran 2 fi+
+.fi
+
+2. Repeat the previous command but begin with a higher order fit
+and run the task in interactive mode in order to examine the fit
+residuals.
+
+.nf
+ cl> wregister radio xray radio.tran wxformat=%12.2H \
+ wyformat=%12.1h xxo=4 xyo=4 xxt=half yxo=4 yyo=4 \
+ yxt=half inter+
+
+ ... a plot of the fit appears
+
+ ... type x and r to examine the residuals of the x
+ surface fit versus x and y
+
+ ... type y and s to examine the residuals of the y
+ surface fit versus x and y
+
+ ... delete 2 deviant points with the d key and
+ recompute the fit with the f key
+
+ ... type q to quit, save the fit, and compute the registered
+ image
+.fi
+
+3. Mosaic a set of 9 images covering a ~ 1 degree field into a single image
+centered at 12:32:53.1 +43:13:03. Set the output image scale to 0.5
+arc-seconds / pixel which is close the detector scale of 0.51 arc-seconds
+per pixel. Set the orientation to be north up and east to the left.
+The 9 images all have accurate world coordinate information in their headers.
+
+.nf
+ # Create a dummy reference image big enough to cover 1 square degree
+
+ cl> mkpattern refimage ncols=7200 nlines=7200 ...
+
+ # Give the dummy reference image the desired coordinate system
+
+ cl> ccsetwcs refimage "" xref=3600.5 yref=3600.5 xmag=-0.5 \
+ ymag=0.5 lngref=12:32:53.1 latref=43:13:03 ...
+
+ # Register the images using constant boundary extension and
+ # set uservalue to some reasonable value outside the good data
+ # range. Note that it may be possible to improve performance by
+ #increasing nxblock and nyblock.
+
+ cl> wregister @inlist refimage @outlist boundary=constant \
+ constant=<uservalue> nxblock=7200 nyblock=1024 ...
+
+ # Combine the images using imcombine
+
+ cl> imcombine @outlist mosaic lthreshold=<uservalue> ...
+
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+imalign,xregister,tprecess,wcsxymatch,geomap,gregister,geotran,wcscopy
+.endhelp
diff --git a/pkg/images/immatch/doc/xregister.hlp b/pkg/images/immatch/doc/xregister.hlp
new file mode 100644
index 00000000..b0690118
--- /dev/null
+++ b/pkg/images/immatch/doc/xregister.hlp
@@ -0,0 +1,707 @@
+.help xregister Dec98 images.immatch
+.ih
+NAME
+xregister -- register 1 and 2D images using X-correlation techniques
+.ih
+USAGE
+xregister input reference regions shifts
+.ih
+PARAMETERS
+.ls input
+The list of input images to be registered.
+.le
+.ls reference
+The list of reference images to which the input images are to be registered.
+The number of reference images must be one or equal to the number of input
+images.
+.le
+.ls regions
+The list of reference image region(s) used to compute the
+x and y shifts.
+\fIRegions\fR may be: 1) a list of one or more image sections
+separated by whitespace, 2) the name of a text file containing a list
+of one or more image sections separated by whitespace and/or newlines,
+3) a string of the form "grid nx ny" defining a grid of nx by ny
+equally spaced and sized image sections spanning the entire image. Shifts are
+computed for each specified region individually and averaged to produce the
+final x and y shift.
+.le
+.ls shifts
+The name of the text file where the computed x and y shifts
+are written. If \fIdatabasefmt\fR is "yes", a single record containing the
+computed x and y shifts for each image region and the final average x and y
+shift is written to a text database file for each input image.
+If \fIdatabasefmt\fR = "no", a single line containing the image name and the
+final average x and y shift is written to a simple text file
+for each input image.
+.le
+.ls output = ""
+The list of output shifted images. If \fIoutput\fR is the NULL string
+then x and y shifts are computed for each input image and written to
+\fIshifts\fR but no output images are written. If \fIoutput\fR is not NULL
+then the number of output images must equal the number of input images.
+.le
+.ls databasefmt = yes
+If \fIdatabasefmt\fR is "yes" the results are written to a text database
+file, otherwise they are written to a simple text file.
+.le
+.ls records = ""
+The list of records to be written to or read from \fIshifts\fR for each
+input image. If \fIrecords\fR is NULL then the output or input record names
+are assumed to be the names of the input images. If \fIrecords\fR is not NULL
+then the record names in \fIrecords\fR are used to write / read the
+records. This parameter is useful for users
+who, wish to compute the x and y shifts using images that have been processed
+in some manner (e.g. smoothed), but apply the computed x and y shifts to the
+original unprocessed images. If more then one record
+with the same name exists in \fIshifts\fR then the most recently written
+record takes precedence. The records parameter is ignored if
+\fIdatabasefmt\fR is "no".
+.le
+.ls append = yes
+Append new records to an existing \fIshifts\fR file or start a new shifts
+file for each execution of XREGISTER? The append parameter is ignored
+if \fIdatabasefmt\fR is "no".
+.le
+.ls coords = ""
+An optional list of coordinates files containing the x and y coordinates of
+an object in the reference image on the first line and the x and y coordinates
+of the same object in the input image(s) on succeeding lines. The number
+of coordinate files must be equal to the number of reference images.
+The input coordinates are used to compute initial
+values for the x and y lags between the input image and the reference image,
+and supersede any non-zero values of \fIxlag\fR, \fIylag\fR, \fIdxlag\fR,
+and \fIdylag\fR supplied by the user.
+.le
+.ls xlag = 0, ylag = 0
+The initial x and y lags of the input image with respect to the reference
+image. Positive values imply that the input image is shifted
+in the direction of increasing x and y values with respect to the
+reference image. \fIXlag\fR and \fIylag\fR are overridden if an offset
+has been determined using the x and y coordinates in the \fIcoords\fR file.
+.le
+.ls dxlag = 0, dylag = 0
+The increment in \fIxlag\fR and \fIylag\fR to be applied to successive input
+images. If \fIdxlag\fR and \fIdylag\fR are set to INDEF then the
+computed x and y lags for the previous image are used as the initial
+x and y lags for the current image. This option is useful for images which
+were taken as a time sequence and whose x and y the shifts increase or
+decrease in a systematic manner.
+\fIDxlag\fR and \fIdylag\fR are overridden if an offset
+has been determined using x and y coordinates in the \fIcoords\fR file.
+.le
+.ls background = none
+The default background function to be subtracted from the input
+and reference image data in each region before the
+cross-correlation function is computed. The options are:
+.ls none
+no background subtraction is done.
+.le
+.ls mean
+the mean of the reference and input image region is computed and subtracted
+from the image data.
+.le
+.ls median
+the median of the reference and input image region is computed and subtracted
+from the data.
+.le
+.ls plane
+a plane is fit to the reference and input image region and subtracted
+from the data.
+.le
+
+By default the cross-correlation function is computed in a manner
+which removes the mean intensity in the reference and input image regions
+from the data. For many data sets this "correction" is sufficient to
+remove first order background level effects
+from the computed cross-correlation function and no additional
+background subtraction is required.
+.le
+.ls border = INDEF
+The width of the border region around the input and reference image data
+regions used to compute the background function if \fIbackground\fR
+is not "none". By default the entire region is used.
+.le
+.ls loreject = INDEF, ls hireject = INDEF
+The k-sigma rejection limits for removing the effects of bad data from the
+background fit.
+.le
+.ls apodize = 0.0
+The fraction of the input and reference image data endpoints in x and y
+to apodize with a
+cosine bell function before the cross-correlation function is computed.
+.le
+.ls filter = none
+The spatial filter to be applied to the reference and input image
+data before the cross-correlation function is computed. The options are:
+.ls none
+no spatial filtering is performed.
+.le
+.ls laplace
+a Laplacian filter is applied to the reference and input image data.
+.le
+.le
+.ls correlation = discrete
+The algorithm used to compute the cross-correlation function. The options
+are:
+.ls discrete
+The cross-correlation function is calculated by computing the discrete
+convolution of the reference and input image regions over the x and y
+window of interest. This technique is most efficient method for small
+cross-correlation function x and y search windows.
+.le
+.ls fourier
+The cross-correlation function is calculated by computing the convolution
+of the reference and input image regions using Fourier techniques.
+This technique is the most efficient method for computing the
+cross-correlation function for small x and y search windows.
+.le
+.ls difference
+The cross-correlation function is calculated by computing the error
+function of the reference and input images as a function of position
+in the x and y search window.
+.le
+.ls file
+No cross-correlation function is computed. Instead the previously
+computed x and y shifts are read from record \fIrecord\fR in the text
+database file \fIshifts\fR if \fIdatabasefmt\fR is "yes", or the
+next line of a simple text file if \fIdatabasefmt\fR is "no".
+.le
+.le
+.ls xwindow = 11, ywindow = 11
+The x and y width of the cross-correlation function region
+to be computed and/or searched for peaks. The search window corresponds
+to shifts of - xwindow / 2 <= xshift <= xwindow /2 and - ywindow / 2 <=
+yshift <= ywindow / 2. \fIXwindow\fR and \fIywindow\fR
+are automatically rounded up to the next nearest odd number.
+.le
+.ls function = centroid
+The algorithm used to compute the x and y position of the cross-correlation
+function peak. The options are:
+.ls none
+the position of the cross-correlation function peak is set to
+x and y position of the maximum pixel.
+.le
+.ls centroid
+the position of the cross-correlation function peak is calculated
+by computing the intensity-weighted mean of the marginal profiles of
+the cross-correlation function in x and y.
+.le
+.ls sawtooth
+the position of the cross-correlation function peak is calculated
+by convolving 1D slices in x and y through the cross-correlation function
+with a 1D sawtooth function and using the point at which the peak is
+bisected to determine the x and y position of the cross-correlation
+peak.
+.le
+.ls parabolic
+a 1D parabola is fit to 1D slices in x and y through the cross-correlation
+function and the fitted coefficients are used to compute the peak of
+the cross-correlation function.
+.le
+.ls mark
+mark the peak of the cross-correlation function with the graphics cursor.
+This option will only work if \fIinteractive\fR = "yes".
+.le
+.le
+.ls xcbox = 5, ycbox = 5
+The width of the box centered on the peak of the cross-correlation function
+used to compute the fractional pixel x and y center.
+.le
+.ls interp_type = "linear"
+The interpolant type use to computed the output shifted image.
+The choices are the following:
+.ls nearest
+nearest neighbor.
+.le
+.ls linear
+bilinear interpolation in x and y.
+.le
+.ls poly3
+third order interior polynomial in x and y.
+.le
+.ls poly5
+fifth order interior polynomial in x and y.
+.le
+.ls spline3
+bicubic spline.
+.le
+.ls sinc
+2D sinc interpolation. Users can specify the sinc interpolant width by
+appending a width value to the interpolant string, e.g. sinc51 specifies
+a 51 by 51 pixel wide sinc interpolant. The sinc width input by the
+user will be rounded up to the nearest odd number. The default sinc width
+is 31 by 31.
+.le
+.ls drizzle
+2D drizzle resampling. Users can specify the drizzle pixel fractions in x and y
+by appending values between 0.0 and 1.0 in square brackets to the
+interpolant string, e.g. drizzle[0.5]. The default value is 1.0. The
+value 0.0 is increased to 0.001. Drizzle resampling with a pixel fraction
+of 1.0 in x and y is identical to bilinear interpolation.
+.le
+.le
+.ls boundary_type = "nearest"
+The boundary extension algorithm used to compute the output shifted
+image. The choices are:
+.ls nearest
+use the value of the nearest boundary pixel.
+.le
+.ls constant
+use a constant value.
+.le
+.ls reflect
+generate a value by reflecting about the boundary.
+.le
+.ls wrap
+generate a value by wrapping around to the opposite side of the image.
+.le
+.le
+.ls constant = 0
+The default constant for constant boundary extension.
+.le
+.ls interactive = no
+Compute the cross-correlation function and the shifts for each image
+interactively using graphics cursor and optionally image cursor input.
+.le
+.ls verbose
+Print messages about the progress of the task during task execution
+in non-interactive mode.
+.le
+.ls graphics = "stdgraph"
+The default graphics device.
+.le
+.ls display = "stdimage"
+The default image display device.
+.le
+.ls gcommands = ""
+The default graphics cursor.
+.le
+.ls icommands = ""
+The default image display cursor.
+.le
+
+.ih
+DESCRIPTION
+
+XREGISTER computes the x and y shifts required to register a list of input
+images \fIinput\fR to a list of reference images \fIreference\fR using
+cross-correlation techniques. The computed x and y shifts are stored
+in the text file \fIshifts\fR, in the records \fIrecords\fR if
+\fIdatabasefmt\fR is "yes" or a single line of a simple text file
+if \fIdatabasefmt\fR is "no". One entry is made in the shifts file for
+each input image. If a non NULL list of output images
+\fIoutput\fR is supplied a shifted output image is written for each input
+image. XREGISTER is intended to solve 1D and 2D image registration problems
+where the images have the same size, the same pixel scale, are shifted
+relative to
+each other by simple translations in x and y, and contain one or more
+extended features in common that will produce a peak in the computed
+cross-correlation function.
+
+The reference image regions used to compute the cross-correlation
+function shifts are defined by the parameter
+\fIregions\fR. \fIRegions\fR may be:
+1) a list of one or more image sections, e.g.
+"[100:200,100:200] [400:500,400:500]" separated
+by whitespace, 2) the name of a text file containing a list of one or
+more image sections separated by whitespace and / or newline characters,
+or, 3) a string
+of the form "grid nx ny" specifying a grid of nx by ny
+image sections spanning the entire reference image.
+All reference image regions should be chosen so as to
+include at least one well-defined object or feature. Cross-correlation
+functions and x and y shifts are computed independently for each
+reference image region
+and averaged to produce the final x and y shift for each input image.
+
+By default the initial x and y lags between the input and reference
+image are assumed to by 0.0 and 0.0
+respectively and each reference image region is cross-correlated
+with the identical region in the input image, e.g reference image
+region [100:200,100:200] is cross-correlated with input image
+region [100:200,100:200].
+
+Non-zero initial guesses for
+the x and y shifts for each input image can be input to XREGISTER using
+the coordinates file parameter \fIcoords\fR.
+\fICoords\fR is a simple text file containing the x
+and y coordinates of a single
+object in the reference image in columns one and two
+of line one, and the x and y coordinates of the same object in the first
+input image in columns one and two of line two, etc. If \fIcoords\fR
+is defined there must be one coordinate file for every reference image.
+If there are fewer lines of text in \fIcoords\fR than there are
+numbers of reference plus input images, then x and y shifts of 0.0 are
+assumed for the extra input images. For example,
+if the user specifies a single input and reference image, sets the
+\fIregions\fR parameter to "[100:200,100:200]", and defines
+a coordinates file which contains the numbers
+50.0 50.0 in columns one and two of line one, and the numbers 52.0 and 52.0
+in columns one and two of line two, then the initial x and y
+lags for the input image with respect to the reference image will be 2.0
+and 2.0 respectively, and the reference image region [100:200,100:200] will be
+cross-correlated with the input image region [102:202,102:202].
+
+If \fIcoords\fR is NULL, the parameters \fIxlag\fR, \fIylag\fR,
+\fIdxlag\fR, and \fIdylag\fR can be used to define initial x and y lags
+for each input image. \fIXlag\fR and \fIylag\fR define the x and y lags
+of the first input image with respect to the reference image. In the
+example above they would be set to 2.0 and 2.0 respectively. Initial
+shifts for succeeding images are computed by adding the values of the
+\fIdxlag\fR and \fIdylag\fR parameters to the values of
+\fIxlag\fR and \fIylag\fR assumed for the previous image.
+If \fIdxlag\fR and \fIdylag\fR are 0.0 and 0.0
+the same initial x and y lag will be used for all the input
+images. If \fIdxlag\fR and \fIdylag\fR are both finite numbers then these
+numbers will be added to
+the x and y lags assumed for the previous image. If these numbers
+are both INDEF then the computed x and y lags for the previous image
+will be used to compute the initial x and y lags for the current image.
+Both options can be useful for time series images where the x and y
+shifts between successive images display some regular behavior.
+
+Prior to computing the cross-correlation function
+large mean background values and gradients should be removed
+from the input and reference image data as either
+can seriously degrade the peak of the cross-correlation
+function. To first order XREGISTER computes the cross-correlation function
+in a manner which removes
+the effect of large mean background values from the resulting
+function. For many if not most typical data sets the user can safely leave
+the parameter \fIbackground\fR at its default value of "none" and
+achieve reasonable results. For more demanding data sets the user should
+experiment with the "mean", "median", and "plane" background fitting
+algorithms which compute and subtract, the mean value, median value, and
+a plane from the input and reference image data respectively,
+before computing the
+cross-correlation function. The region used to compute the background fitting
+function can be restricted to a border around the reference and
+input image regions by setting the \fIborder\fR parameter. Bad
+data can be rejected from the background fit by setting the \fIloreject\fR
+and \fIhireject\fR parameters.
+
+A cosine bell function can be applied to the edges of the input and
+reference image data before
+computing the cross-correlation function by setting the \fIapodize\fR
+parameter.
+
+If the \fIfilter\fR parameter is set to "laplace" instead of its default
+value of "none" then a Laplacian filter is applied to the input and
+reference image data before the cross-correlation function is computed.
+This spatial filtering operation effectively
+removes both a background and a slope from the input and reference image
+data and
+highlights regions of the image where the intensity is changing rapidly.
+The effectiveness of this filtering operation in sharpening the
+correlation peak depends on the degree to
+which the intensity in adjacent pixels is correlated.
+
+The cross-correlation function for each region is computed by
+discrete convolution, \fIcorrelation\fR = "discrete",
+Fourier convolution, \fIcorrelation\fR = "fourier", or by computing
+the error function, \fIcorrelation\fR = "difference". The x and y lag
+space in pixels around the initial x and y lag over which the cross-correlation
+function is searched for the correlation peak, is specified by the
+\fIxwindow\fR and
+\fIywindow\fR parameters. These parameter define a range of x and y lags from
+-xwindow / 2 to xwindow / 2 and -ywindow / 2 to ywindow / 2 respectively. For
+a given input and reference image region, the
+execution time of XREGISTER will depend strongly on both the correlation
+algorithm chosen and
+the size of the search window. In general users should use discrete
+or difference correlation for small search windows and fourier
+correlation for large search windows.
+
+The x and y lags for each input and reference image
+region are computed by computing
+the position of the peak of the cross-correlation function in the
+search window using
+one of the four centering algorithms: "none", "centroid", "sawtooth",
+and "parabolic".
+
+The computed x and y shifts for each region and the final x and y shift
+for each input image (where the computed x and y shifts are just the negative
+of the computed x and y lags) are written to the shifts file \fIshifts\fR.
+If \fIdatabasefmt\fR is "yes" each results is written in a record whose name
+is either identical to the name of the input
+image or supplied by the user via the \fIrecords\fR parameter .
+If \fIdatabasefmt\fR is "no", then a single containing the input image
+name and the computed x and y shifts is written to the output shifts file.
+
+If a list of output image names have been supplied then the x and y
+shifts will be applied to the input images to compute the output images
+using the interpolant type specified by \fIinterp_type\fR and the
+boundary extension algorithm specified by \fIboundary\fR and \fIconstant\fR.
+
+If the \fIcorrelation\fR parameter is set to "file" then the shifts
+computed in a previous run of XREGISTER will be read from the \fIshifts\fR
+file and applied to the input images to compute the output images.
+If no record list is supplied by the user XREGISTER will for each input
+image search for
+a record whose name is the same as the input image name. If more than
+one record of the same name is found then the most recently written
+record will be used.
+
+XREGISTER does not currently trim the input images but it computes and
+prints the region over which they all overlap in the form of an image
+section. Although XREGISTER is designed for use with same sized images,
+it may be used with images of varying size.
+In this case it is possible for the calculated overlap region to be vignetted,
+as XREGISTER currently preserves the size of the input image when it shifts it.
+For example if an image is much smaller than the reference image
+it is possible for the image to be shifted outside of its own borders.
+If the smallest image is used as a reference this will not occur. If
+vignetting is detected the vignetted image section is printed on the
+screen. Vignetting may also occur for a list of same-sized images
+if the reference image is not included in the input image list, and the
+computed shifts are all positive or negative as may occur in a time
+sequence. Choosing a reference image with a shift which is in the
+middle of the observed range of shifts in x and y will remove this problem.
+
+In non-interactive mode the parameters are set at task startup
+and the input images are processed sequentially. If the \fIverbose\fR
+flag is set messages about the progress of the task are printed on the
+screen as the task is running.
+
+In interactive mode the user can mark the regions to be used
+to compute the cross-correlation function on the image display,
+define the initial shifts from the reference image to the input image
+on the image display, show/set the data and algorithm parameters,
+compute, recompute, and plot the cross-correlation function, experiment
+with the various peak fitting algorithms, and overlay row and column
+plots of the input and reference images with and without the initial and / or
+computed shifts factored in.
+
+.ih
+CURSOR COMMANDS
+
+The following graphics cursor commands are currently available in
+XREGISTER.
+
+
+.nf
+ Interactive Keystroke Commands
+
+? Print help
+: Colon commands
+t Define the offset between the reference and the input image
+c Draw a contour plot of the cross-correlation function
+x Draw a column plot of the cross-correlation function
+y Draw a line plot of the cross-correlation function
+r Redraw the current plot
+f Recompute the cross-correlation function
+o Enter the image overlay plot submenu
+w Update the task parameters
+q Exit
+
+
+ Colon Commands
+
+:mark Mark regions on the display
+:show Show the current values of the parameters
+
+ Show/Set Parameters
+
+:reference [string] Show/set the current reference image name
+:input [string] Show/set the current input image name
+:regions [string] Show/set the regions list
+:shifts {string] Show/set the shifts database file name
+:coords [string] Show/set the current coordinates file name
+:output [string] Show/set the current output image name
+:records [string] Show/set the current database record name
+:xlag [value] Show/set the initial lag in x
+:ylag [value] Show/set the initial lag in y
+:dxlag [value] Show/set the incremental lag in x
+:dylag [value] Show/set the incremental lag in y
+:cregion [value] Show/set the current region
+:background [string] Show/set the background fitting function
+:border [value] Show/set border region for background fitting
+:loreject [value] Show/set low side k-sigma rejection
+:hireject [value] Show/set high side k-sigma rejection
+:apodize [value] Show/set percent of end points to apodize
+:filter [string] Show/set the default spatial filter
+:correlation [string] Show/set cross-correlation function
+:xwindow [value] Show/set width of correlation window in x
+:ywindow [value] Show/set width of correlation window in y
+:function [string] Show/set correlation peak centering function
+:xcbox [value] Show/set the centering box width in x
+:ycbox [value] Show/set the centering box width in y
+.fi
+
+
+The following submenu of image cursor commands is also available.
+
+.nf
+ Image Overlay Plot Submenu
+
+
+? Print help
+c Overlay the marked column of the reference image
+ with the same column of the input image
+l Overlay the marked line of the reference image
+ with the same line of the input image
+x Overlay the marked column of the reference image
+ with the x and y lagged column of the input image
+y Overlay the marked line of the reference image
+ with the x and y lagged line of the input image
+v Overlay the marked column of the reference image
+ with the x and y shifted column of the input image
+h Overlay the marked line of the reference image
+ with the x and y shifted line of the input image
+q Quit
+
+
+ Image Overlay Sub-menu Colon Commands
+
+:c [m] [n] Overlay the middle [mth] column of the reference image
+ with the mth [nth] column of the input image
+:l [m] [n] Overlay the middle [mth] line of the reference image
+ with the mth [nth] line of the input image
+:x [m] Overlay the middle [mth] column of the reference image
+ with the x and y lagged column of the input image
+:y [m] Overlay the middle [mth] line of the reference image
+ with the x and y lagged line of the input image
+:v [m] Overlay the middle [mth] column of the reference image
+ with the x and y shifted column of the input image
+:h [m] Overlay the middle [mth] line of the reference image
+ with the x and y shifted line of the input image
+.fi
+
+.ih
+ALGORITHMS
+
+The cross-correlation function is computed in the following manner.
+The symbols I and R refer to the input and reference images respectively.
+
+.nf
+correlation = discrete
+
+ <I> = SUMj SUMi { I[i+xlag,j+ylag] } / (Nx * Ny)
+ <R> = SUMj SUMi { R[i,j] } / (Nx * Ny)
+ sumsqI = sqrt (SUMj SUMi { (I[i+xlag,j+ylag] - <I>) ** 2 })
+ sumsqR = sqrt (SUMj SUMi { (R[i,j] - <R>) ** 2 })
+
+ X = SUMj SUMi { (I[i+xlag,j+ylag] - <I>) * (R[i,j] - <R>) }
+ ----------------------------------------------------
+ sumsqI * sumsqR
+
+
+correlation = fourier
+
+ <I> = SUMj SUMi { I[i,j] } / (Nx * Ny)
+ <R> = SUMj SUMi { R[i,j] } / (Nx * Ny)
+ sumsqI = sqrt (SUMj SUMi { (I[i,j] - <I>) ** 2 })
+ sumsqR = sqrt (SUMj SUMi { (R[i,j] - <R>) ** 2 })
+ FFTI = FFT { (I - <I>) / sumsqI }
+ FFTR = FFT { (R - <R>) / sumsqR }
+
+ X = FFTINV { FFTR * conj { FFTI } }
+
+
+correlation = difference
+
+ <I> = SUMj SUMi { I[i+xlag,j+ylag] } / (Nx * Ny)
+ <R> = SUMj SUMi { R[i,j] } / (Nx * Ny)
+
+ X = SUMj SUMi { abs ((I[i+xlag,j+ylag] - <I>) - (R[i,j] - <R>)) }
+ X = 1.0 - X / max { X }
+.fi
+
+.ih
+EXAMPLES
+
+1. Register a list of images whose dimensions are all 256 by 256 pixels
+and whose shifts with respect to the reference image are all less than
+5.0 pixels, using the discrete cross-correlation algorithm and a search
+window of 21 pixels in x and y.
+
+.nf
+ cl> xregister @inimlist refimage [*,*] shifts.db out=@outimlist \
+ xwindow=21 ywindow=21
+.fi
+
+2. Register the previous list of images, but compute the cross_correlation
+function using boxcar smoothed versions of the input images.
+
+.nf
+ cl> xregister @binimlist brefimage [*,*] shifts.db xwindow=21 \
+ ywindow=21
+
+ cl> xregister @inimlist refimage [*,*] shifts.db out=@outimlist \
+ records=@binimlist correlation=file
+.fi
+
+3. Register the previous list of images but write the results to a simple
+text file instead of a text database file and do the actual shifting with
+the imshift task.
+
+.nf
+ cl> xregister @binimlist brefimage [*,*] shifts.db xwindow=21 \
+ ywindow=21 databasefmt-
+
+ cl> fields shifts.db 2,3 > shifts
+
+ cl> imshift @inimlist @outimlist shifts_file=shifts
+.fi
+
+4. Register list of 512 by 512 pixel square solar sunspot images that were
+observed as a time series. Compute the cross-correlation function using
+Fourier techniques, a search window of 21 pixels in x and y, an initial
+shift of 10 pixels in x and 1 pixel in y, and use the computed shift of
+the previous image as the initial guess for the current image.
+
+.nf
+ cl> xregister @inimlist refimage [*,*] shifts.db out=@outimlist \
+ xlag=10 ylag=1 dxlag=INDEF dylag=INDEF correlation=fourier \
+ xwindow=21 ywindow=21
+.fi
+
+5. Register two 2K square images interactively using discrete cross-correlation
+and an initial search window of 15 pixels in x and y.
+
+.nf
+ cl> display refimage
+
+ cl> xregister inimage refimage [900:1100,900:1100] shifts.db \
+ xwindow=15 ywindow=15 interactive+
+
+ ... a contour plot of the cross-correlation function appears
+ with the graphics cursor ready to accept commands
+
+ ... type x and y to get line and column plots of the cross-
+ correlation function at various points and c to return
+ to the default contour plot
+
+ ... type ? to get a list of the available commands
+
+ ... type :mark to mark a new region on the image display
+
+ ... type f to recompute the cross-correlation function using
+ the new data
+
+ ... increase the search window to 21 pixels in x and y
+ with the :xwindow 21 and :ywindow 21 commands
+
+ ... type f to recompute the cross-correlation function with the
+ new search window
+
+ ... type o to enter the image data overlay plot submenu,
+ move the cursor to a line in the displayed reference image
+ and type l to see of plot of the line in the input and
+ reference image, type h to see a plot of the same line in
+ the reference image and the x and y shifted line in the input
+ image, type q to return to the main menu
+
+ ... type q to quit the task, and q again to verify the previous
+ q command
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+rv.fxcor,proto.imalign,images.imcombine,ctio.immatch,center1d,images.imshift
+.endhelp
diff --git a/pkg/images/immatch/doc/xyxymatch.hlp b/pkg/images/immatch/doc/xyxymatch.hlp
new file mode 100644
index 00000000..82a8c8bb
--- /dev/null
+++ b/pkg/images/immatch/doc/xyxymatch.hlp
@@ -0,0 +1,468 @@
+.help xyxymatch Jul95 images.immatch
+.ih
+NAME
+xyxymatch -- Match pixel coordinate lists using various methods
+.ih
+USAGE
+xyxymatch input reference output tolerance
+.ih
+PARAMETERS
+.ls input
+The list of input coordinate files. The input file is a whitespace-delimited
+text table containing the coordinates. The \fIxcolumn\fR and \fIycolumn\fR
+parameters define the coordinate columns to be used.
+.le
+.ls reference
+The list of reference coordinate files. The number of reference coordinate
+files must be one or equal to the number of input coordinate files.
+The reference file is a whitespace-delimited
+text table containing the coordinates. The \fIxrcolumn\fR and \fIyrcolumn\fR
+parameters define the coordinate columns to be used.
+.le
+.ls output
+The output matched x-y lists containing three pairs of numbers: the coordinates
+of the object in the reference list in columns 1 and 2, the
+coordinates of the object in the input list in columns 3 and 4, and
+the line number of the objects in the original reference and input
+lists in columns 5 and 6.
+.le
+.ls tolerance
+The matching tolerance in pixels.
+.le
+.ls refpoints = ""
+The list of tie points used to compute the linear transformation
+from the input coordinate system to the reference coordinate system. Refpoints
+is a text file containing the x-y coordinates of 1-3 reference list tie points
+in the first line, followed by the x-y coordinates of the 1-3 corresponding
+input tie points in succeeding
+lines. If refpoints is undefined then the parameters \fIxin\fR, \fIyin\fR,
+\fIxmag\fR, \fIymag\fR, \fIxrotation\fR, \fIyrotataion\fR, \fIxref\fR,
+and \fIyref\fR are used to compute the linear transformation from the
+input coordinate system to the reference coordinate system.
+.le
+.ls xin = INDEF, yin = INDEF
+The x and y origin of the input coordinate system. Xin and yin default to
+0.0 and 0.0 respectively.
+.le
+.ls xmag = INDEF, ymag = INDEF
+The x and y scale factors in reference pixels per input pixels. Xmag and
+ymag default to 1.0 and 1.0 respectively.
+.le
+.ls xrotation = INDEF, yrotation = INDEF
+The x and y rotation angles measured in degrees counter-clockwise with
+respect to the x axis. Xrotation and yrotation default to 0.0 and 0.0
+respectively.
+.le
+.ls xref = INDEF, yref = INDEF
+The x and y origin of the reference coordinate system. Xref and yref default
+to 0.0 and 0.0 respectively.
+.le
+.ls xcolumn = 1, ycolumn = 2
+The columns in the input coordinate list containing the x and y coordinate
+values respectively.
+.le
+.ls xrcolumn = 1, yrcolumn = 2
+The columns in the reference coordinate list containing the x and y coordinate
+values respectively.
+.le
+.ls separation = 9.0
+The minimum separation for objects in the input and reference coordinate
+lists. Objects closer together than separation pixels
+are removed from the input and reference coordinate lists prior to matching.
+.le
+.ls matching = "triangles"
+The matching algorithm. The choices are:
+.ls tolerance
+A linear transformation is applied to the input coordinate list,
+the transformed input list and the reference list are sorted,
+points which are too close together are removed, and the input coordinates
+which most closely match the reference coordinates within the
+user specified tolerance are determined. The tolerance algorithm requires
+an initial estimate for the linear transformation. This estimate can be
+derived interactively by pointing to common objects in the two displayed
+images, by supplying the coordinates of tie points via the
+\fIrefpoints\fR file, or by setting the linear transformation parameters
+\fIxin\fR, \fIyin\fR, \fIxmag\fR, \fIymag\fR, \fIxrotation\fR,
+\fIyrotation\fR, \fIxref\fR, and \fIyref\fR. Assuming that
+well chosen tie points are supplied, the tolerance algorithm
+functions well in the presence of any shifts, axis flips, x and y
+scale changes, rotations, and axis skew, between the two coordinate
+systems. The algorithm is sensitive to higher order distortion terms
+in the coordinate transformation.
+.le
+.ls triangles
+A linear transformation is applied to the input coordinate list,
+the transformed input list and the reference list are sorted, points
+which are too close together are removed, and the input coordinates
+are matched to the reference coordinates using a triangle pattern
+matching technique and the user specified tolerance parameter.
+The triangles pattern matching algorithm does not require prior knowledge
+of the linear transformation, although it will use one if one is supplied.
+The algorithm functions well in the presence of
+any shifts, axis flips, magnification, and rotation between the two coordinate
+systems as long as both lists have a reasonable number of objects
+in common and the errors in the computed coordinates are small.
+However since the algorithm depends on comparisons of similar triangles, it
+is sensitive to differences in the x and y coordinate scales,
+any skew between the x and y axes, and higher order distortion terms
+in the coordinate transformation.
+.le
+.le
+.ls nmatch = 30
+The maximum number of reference and input coordinates used
+by the "triangles" pattern matching algorithm. If either list contains
+more coordinates than nmatch the lists are subsampled. Nmatch should be
+kept small as the computation and memory requirements of the "triangles"
+algorithm depend on a high power of the lengths of the respective lists.
+.le
+.ls ratio = 10.0
+The maximum ratio of the longest to shortest side of the
+triangles generated by the "triangles" pattern matching algorithm.
+Triangles with computed longest to shortest side ratios > ratio
+are rejected from the pattern matching algorithm. \fIratio\fR should never
+be set higher than 10.0 but may be set as low as 5.0.
+.le
+.ls nreject = 10
+The maximum number of rejection iterations for the "triangles" pattern
+matching algorithm.
+.le
+.ls xformat = "%13.3f", yformat = "%13.3f"
+The format of the output reference and input x and y coordinates.
+By default the coordinates are output right justified in a field of
+13 characters with 3 places following the decimal point.
+.le
+.ls interactive = no
+Compute the initial linear transformation required to transform the
+input coordinate coordinates to the reference coordinate system, by defining
+up to three tie points using the image display and the image cursor.
+.le
+.ls verbose = yes
+Print messages about the progress of the task ?
+.le
+.ls icommands = ""
+The image display cursor.
+.le
+
+.ih
+DESCRIPTION
+
+XYXYMATCH matches the x and y coordinates in the reference coordinate list
+\fIreference\fR to the corresponding x and y coordinates in the input
+coordinate list \fIinput\fR to within a user specified tolerance
+\fItolerance\fR, and writes the matched coordinates to the output
+file \fIoutput\fR. The output file is suitable for input to the
+GEOMAP task which computes the actual transformation required to
+register the corresponding reference and input images.
+
+XYXYMATCH matches the coordinate lists by: 1) computing an initial
+guess at the linear transformation required to match the input
+coordinate system to the reference coordinate system, 2) applying
+the computed transformation to the input coordinates, 3) sorting
+the reference and input coordinates and removing points with a
+minimum separation specified by the parameter \fIseparation\fR
+from both lists, 4) matching the two lists using either the "tolerance"
+or "triangles" algorithm, and 5) writing the matched list to the
+output file.
+
+The initial estimate of the linear transformation is computed in one of
+three ways. If \fIinteractive\fR is "yes" the user displays the reference and
+input images corresponding to the reference and input coordinate files
+on the image display, and marks up to three objects which the two
+images have in common with the image cursor. The coordinates of these
+tie points are used as tie points to compute the linear transformation.
+If \fIrefpoints\fR is defined, the x-y coordinates of up to three tie
+points are read from succeeding lines in the refpoints file. The format
+of two sample refpoints files is shown below.
+
+.nf
+# First sample refpoints file (1 reference file and N input files)
+
+x1 y1 [x2 y2 [x3 y3]] # tie points for reference coordinate file
+x1 y1 [x2 y2 [x3 y3]] # tie points for input coordinate file 1
+x1 y1 [x2 y2 [x3 y3]] # tie points for input coordinate file 2
+...
+x1 y1 [x2 y2 [x3 y3]] # tie points for input coordinate file N
+
+
+# Second sample refpoints file (N reference files and N input files)
+
+x1 y1 [x2 y2 [x3 y3]] # tie points for reference coordinate file 1
+x1 y1 [x2 y2 [x3 y3]] # tie points for input coordinate file 1
+x1 y1 [x2 y2 [x3 y3]] # tie points for reference coordinate file 2
+x1 y1 [x2 y2 [x3 y3]] # tie points for input coordinate file 2
+...
+x1 y1 [x2 y2 [x3 y3]] # tie points for reference coordinate file N
+x1 y1 [x2 y2 [x3 y3]] # tie points for input coordinate file N
+
+.fi
+
+The coordinates of the tie points can be typed in by hand if \fIrefpoints\fR
+is "STDIN". If the refpoints file is undefined the parameters
+\fIxin\fR, \fIxin\fR, \fIxmag\fR, \fIymag\fR, \fIxrotation\fR, \fIyrotation\fR,
+\fIxref\fR, and \fIyref\fR are used to compute the linear transformation
+from the input coordinates [xi,yi] to the reference coordinates [xr,yr]
+as shown below. Orientation and skew are the orientation of the x and y axes
+and their deviation from non-perpendicularity respectively.
+
+.nf
+ xr = a + b * xi + c * yi
+ yr = d + e * xi + f * yi
+
+ xrotation = orientation - skew / 2
+ yrotation = orientation + skew / 2
+ b = xmag * cos (xrotation)
+ c = -ymag * sin (yrotation)
+ e = xmag * sin (xrotation)
+ f = ymag * cos (yrotation)
+ a = xref - b * xin - c * yin = xshift
+ d = yref - e * xin - f * yin = yshift
+.fi
+
+The reference and input coordinates are read from columns \fIxrcolumn\fR,
+\fIyrcolumn\fR in the reference, and \fIxcolumn\fR, and \fIycolumn\fR in the
+input coordinate lists respectively. The input coordinates are transformed
+using the computed linear transformation and stars closer together than
+\fIseparation\fR pixels are removed from both lists.
+
+The coordinate lists are matched using the algorithm specified by
+the \fImatching\fR
+parameter. If matching is "tolerance", XYXYMATCH searches the sorted
+transformed input coordinate list for the object closest to the current
+reference object within the matching tolerance \fItolerance\fR.
+The major advantage of the "tolerance" algorithm is that it can deal
+with x and y scale differences and axis skew in the coordinate
+transformation. The major disadvantage is that the user must supply
+tie point information in all but the simplest case of small x and y
+shifts between the input and reference coordinate systems.
+
+If matching is "triangles" XYXYMATCH constructs a list of triangles
+using up to \fInmatch\fR reference coordinates and transformed input
+coordinates, and performs a pattern matching operation on the resulting
+triangle lists. If the number of coordinates
+in both lists is less than \fInmatch\fR the entire list is matched using
+the "triangles" algorithm directly, otherwise the "triangles" algorithm
+is used to estimate a new linear transformation, the input coordinate
+list is transformed using the new transformation, and the entire list
+is matched using the "tolerance" algorithm. The major advantage of the
+"triangles" algorithm is that it requires no tie point information
+from the user. The major disadvantages are that it is sensitive to
+x and y scale differences and axis skews between the input and reference
+coordinate systems and can be computationally expensive.
+
+The matched x and y reference and input coordinate lists are written to
+columns 1 and 2, and 3 and 4 of the output file respectively, in a format
+specified by the \fIxformat\fR and \fIyformat\fR parameters.
+The respective line numbers in the original reference and input
+coordinate files are written to columns 5 and 6 respectively.
+
+If \fIverbose\fR is yes, detailed messages about actions taken by the
+task are written to the terminal as the task executes.
+
+.ih
+ALGORITHMS
+
+The "triangles" algorithm uses a sophisticated pattern matching
+technique which requires no tie point information from the user.
+It is expensive computationally and hence is restricted to a maximum
+of \fInmatch\fR objects from the reference and input coordinate lists.
+
+The "triangles" algorithm first generates a list
+of all the possible triangles that can be formed from the points in each list.
+For a list of nmatch points this number is the combinatorial factor
+nmatch! / [(nmatch-3)! * 3!] or nmatch * (nmatch-1) * (nmatch-2) / 6.
+The length of the perimeter, ratio of longest to shortest side, cosine
+of the angle between the longest and shortest side, the tolerances in
+the latter two quantities and the direction of the arrangement of the vertices
+of each triangle are computed and stored in a table.
+Triangles with vertices closer together than \fItolerance\fR or
+with a ratio of the longest to shortest side greater than \fIratio\fR
+are discarded. The remaining triangles are sorted in order of increasing
+ratio. A sort merge algorithm is used to match the triangles using the
+ratio and cosine information, the tolerances in these quantities, and
+the maximum tolerances for both lists. Next the ratios of the
+perimeters of the matched triangles are compared to the average ratio
+for the entire list, and triangles which deviate too widely from the mean
+are discarded. The number of triangles remaining are divided into
+the number which match in the clockwise sense and the number which match
+in the counter-clockwise sense. Those in the minority category
+are eliminated.
+The rejection step can be repeated up to \fInreject\fR times or until
+no more rejections occur whichever comes first.
+The last step in the algorithm is a voting procedure in which each remaining
+matched triangle casts three votes, one for each matched pair of vertices.
+Points which have fewer than half the maximum number of
+votes are discarded. The final set of matches are written to the output file.
+
+The "triangles" algorithm functions well when the reference and
+input coordinate lists have a sufficient number of objects (~50%,
+in some cases as low as 25%) of their objects in common, any distortions
+including x and y scale differences and skew between the two systems are small,
+and the random errors in the coordinates are small. Increasing the value of the
+\fItolerance\fR parameter will increase the ability to deal with distortions but
+will also produce more false matches.
+
+.ih
+FORMATS
+
+A format specification has the form "%w.dCn", where w is the field
+width, d is the number of decimal places or the number of digits of
+precision, C is the format code, and n is radix character for
+format code "r" only. The w and d fields are optional. The format
+codes C are as follows:
+
+.nf
+b boolean (YES or NO)
+c single character (c or '\c' or '\0nnn')
+d decimal integer
+e exponential format (D specifies the precision)
+f fixed format (D specifies the number of decimal places)
+g general format (D specifies the precision)
+h hms format (hh:mm:ss.ss, D = no. decimal places)
+m minutes, seconds (or hours, minutes) (mm:ss.ss)
+o octal integer
+rN convert integer in any radix N
+s string (D field specifies max chars to print)
+t advance To column given as field W
+u unsigned decimal integer
+w output the number of spaces given by field W
+x hexadecimal integer
+z complex format (r,r) (D = precision)
+
+
+
+Conventions for w (field width) specification:
+
+ W = n right justify in field of N characters, blank fill
+ -n left justify in field of N characters, blank fill
+ 0n zero fill at left (only if right justified)
+absent, 0 use as much space as needed (D field sets precision)
+
+Escape sequences (e.g. "\n" for newline):
+
+\b backspace (not implemented)
+\f formfeed
+\n newline (crlf)
+\r carriage return
+\t tab
+\" string delimiter character
+\' character constant delimiter character
+\\ backslash character
+\nnn octal value of character
+
+Examples
+
+%s format a string using as much space as required
+%-10s left justify a string in a field of 10 characters
+%-10.10s left justify and truncate a string in a field of 10 characters
+%10s right justify a string in a field of 10 characters
+%10.10s right justify and truncate a string in a field of 10 characters
+
+%7.3f print a real number right justified in floating point format
+%-7.3f same as above but left justified
+%15.7e print a real number right justified in exponential format
+%-15.7e same as above but left justified
+%12.5g print a real number right justified in general format
+%-12.5g same as above but left justified
+
+%h format as nn:nn:nn.n
+%15h right justify nn:nn:nn.n in field of 15 characters
+%-15h left justify nn:nn:nn.n in a field of 15 characters
+%12.2h right justify nn:nn:nn.nn
+%-12.2h left justify nn:nn:nn.nn
+
+%H / by 15 and format as nn:nn:nn.n
+%15H / by 15 and right justify nn:nn:nn.n in field of 15 characters
+%-15H / by 15 and left justify nn:nn:nn.n in field of 15 characters
+%12.2H / by 15 and right justify nn:nn:nn.nn
+%-12.2H / by 15 and left justify nn:nn:nn.nn
+
+\n insert a newline
+.fi
+
+.ih
+REFERENCES
+
+A detailed description of the "triangles" pattern matching algorithm used here
+can be found in the article "A Pattern-Matching Algorithm for Two-
+Dimensional Coordinate Lists" by E.J. Groth, A.J. 91, 1244 (1986).
+
+.ih
+EXAMPLES
+
+1. Match the coordinate list of an image to the coordinate list of a reference
+image using the triangles matching algorithm and a tolerance of 3 pixels.
+Use the resulting matched list to compute the transformation
+required to register the input image lpix to the reference image.
+For completeness this example demonstrates how the individual input
+and reference coordinate lists can be generated.
+
+.nf
+ cl> imlintran dev$pix[-*,*] lpix xrot=15 yrot=15 xmag=1.2 \
+ ymag=1.2 xin=INDEF yin=INDEF xref=265.0 yref=265.0 \
+ ncols=INDEF nlines=INDEF
+
+ cl> daofind dev$pix fwhm=2.5 sigma=5.0 threshold=100.0
+ cl> daofind lpix fwhm=2.5 sigma=5.0 threshold=100.0
+
+ cl> xyxymatch lpix.coo.1 pix.coo.1 xymatch toler=3 \
+ matching=triangles
+
+ cl> geomap xymatch geodb 1.0 512.0 1.0 512.0
+.fi
+
+2. Match the coordinate lists above using the tolerance matching algorithm
+and the image display and cursor.
+
+.nf
+ cl> display dev$pix 1 fi+
+ cl> display lpix 2 fi+
+
+ cl> xyxymatch lpix.coo.1 pix.coo.1 xymatch toler=3 \
+ matching=tolerance interactive+
+
+ ... Mark three points in the reference image dev$pix
+ ... Mark three points in the input image lpix
+
+ cl> geomap xymatch geodb 1.0 512.0 1.0 512.0
+.fi
+
+3. Repeat example 2 but run xyxymatch non-interactively by setting the
+appropriate linear transformation parameters rather than marking stars
+on the image display.
+
+.nf
+ cl> ...
+
+ cl> xyxymatch lpix.coo.1 pix.coo.1 xymatch toler=3 \
+ matching=tolerance xmag=1.2 ymag=1.2 xrot=165 \
+ yrot=345 xref=646.10 yref=33.38
+
+ cl> geomap xymatch geodb 1.0 512.0 1.0 512.0
+.fi
+
+4. Repeat example 2 but run xyxymatch non-interactively
+inputting the appropriate linear transformation via a list of tie points
+rather than marking stars on the image display or creating a refpoints
+file.
+
+.nf
+ cl> ...
+
+ cl> type refpts
+ 442.0 409.0 380.0 66.0 69.0 460.0
+ 82.0 347.0 207.0 84.0 371.0 469.0
+
+ cl> xyxymatch lpix.coo.1 pix.coo.1 xymatch toler=3 \
+ refpoints=refpts matching=tolerance
+
+ cl> geomap xymatch geodb 1.0 512.0 1.0 512.0
+.fi
+
+.ih
+TIME REQUIREMENTS
+.ih
+BUGS
+.ih
+SEE ALSO
+daophot.daofind,lintran,imlintran,geomap,register,geotran
+.endhelp
diff --git a/pkg/images/immatch/geomap.par b/pkg/images/immatch/geomap.par
new file mode 100644
index 00000000..27f46bc3
--- /dev/null
+++ b/pkg/images/immatch/geomap.par
@@ -0,0 +1,32 @@
+# GEOMAP Parameters
+
+# Required parameters
+input,f,a,,,,The input coordinate files
+database,f,a,,,,The output database file
+xmin,r,a,INDEF,,,Minimum x reference coordinate value
+xmax,r,a,INDEF,,,Maximum x reference coordinate value
+ymin,r,a,INDEF,,,Minimum y reference coordinate value
+ymax,r,a,INDEF,,,Maximum y reference coordinate value
+transforms,s,h,"",,,The output transform records names
+results,f,h,"",,,The optional results summary files
+fitgeometry,s,h,"general",|shift|xyscale|rotate|rscale|rxyscale|general|,,Fitting geometry
+
+# Surface fitting parameters
+function,s,h,"polynomial",|chebyshev|legendre|polynomial|,,Surface type
+xxorder,i,h,2,2,,Order of x fit in x
+xyorder,i,h,2,2,,Order of x fit in y
+xxterms,s,h,"half","|none|half|full|",,X fit cross terms type
+yxorder,i,h,2,2,,Order of y fit in x
+yyorder,i,h,2,2,,Order of y fit in y
+yxterms,s,h,"half","|none|half|full|",,Y fit cross terms type
+maxiter,i,h,0,,,Maximum number of rejection iterations
+reject,r,h,3.0,,,Rejection limit in sigma units
+calctype,s,h,"real",|real|double|,,'Computation type'
+
+# Output and graphics parameters
+verbose,b,h,yes,,,Print messages about progress of task ?
+interactive,b,h,yes,,,Fit transformation interactively ?
+graphics,s,h,"stdgraph",,,Default graphics device
+cursor,*gcur,h,,,,Graphics cursor
+
+mode,s,h,'ql'
diff --git a/pkg/images/immatch/geotran.par b/pkg/images/immatch/geotran.par
new file mode 100644
index 00000000..dcae9d1c
--- /dev/null
+++ b/pkg/images/immatch/geotran.par
@@ -0,0 +1,45 @@
+# GEOTRAN Parameters
+
+# required parameters
+input,f,a,,,,Input data
+output,f,a,,,,Output data
+database,f,a,,,,Name of GEOMAP database file
+transforms,s,a,,,,Names of coordinate transforms in database file
+
+# change transformation parameters
+geometry,s,h,"geometric",|linear|geometric|,,"Transformation type (linear,geometric)"
+xin,r,h,INDEF,1.,,X origin of input frame in pixels
+yin,r,h,INDEF,1.,,Y origin of input frame in pixels
+xshift,r,h,INDEF,,,X origin shift in pixels
+yshift,r,h,INDEF,,,Y origin shift in pixels
+xout,r,h,INDEF,1.,,X origin of output frame in reference units
+yout,r,h,INDEF,1.,,Y origin of output frame in reference units
+xmag,r,h,INDEF,,,X scale of input picture in pixels per reference unit
+ymag,r,h,INDEF,,,Y scale of input picture in pixels per reference unit
+xrotation,r,h,INDEF,,,X axis rotation in degrees
+yrotation,r,h,INDEF,,,Y axis rotation in degrees
+
+# output picture format parameters
+xmin,r,h,INDEF,,,Minimum reference x value of output picture
+xmax,r,h,INDEF,,,Maximum reference x value of output picture
+ymin,r,h,INDEF,,,Minimum reference y value of output picture
+ymax,r,h,INDEF,,,Maximum reference y value of output picture
+xscale,r,h,1.0,0.0,,X scale of output picture in reference units per pixel
+yscale,r,h,1.0,0.0,,Y scale of output picture in reference units per pixel
+ncols,i,h,INDEF,1,,Number of columns in the output picture
+nlines,i,h,INDEF,1,,Number of lines in the output picture
+
+# coordinate surface and image interpolation parameters
+xsample,r,h,1.,1.,,Coordinate surface sampling interval in x
+ysample,r,h,1.,1.,,Coordinate surface sampling interval in y
+interpolant,s,h,"linear",,,"Interpolant"
+boundary,s,h,"nearest",|nearest|constant|reflect|wrap|,,"Boundary extension (nearest,constant,reflect,wrap)"
+constant,r,h,0.,,,Constant boundary extension
+fluxconserve,b,h,yes,,,Preserve image flux?
+
+# working blocksize
+nxblock,i,h,512,,,X dimension of working block size in pixels
+nyblock,i,h,512,,,Y dimension of working block size in pixels
+verbose,b,h,yes,,,Print messages about the progress of the task
+
+mode,s,h,'ql'
diff --git a/pkg/images/immatch/geoxytran.par b/pkg/images/immatch/geoxytran.par
new file mode 100644
index 00000000..c0b96ac0
--- /dev/null
+++ b/pkg/images/immatch/geoxytran.par
@@ -0,0 +1,28 @@
+# Parameter set for the GEOXYTRAN Task
+
+input,s,a,,,,Input coordinate files to be transformed
+output,s,a,,,,Output transformed coordinate files
+database,f,a,,,,The GEOMAP database file
+transforms,s,a,,,,Names of the coordinate transforms in the database
+
+geometry,s,h,"geometric",|linear|geometric|,,'Transformation type (linear,geometric)'
+direction,s,h,"forward","forward|backward",,Transformation direction (forward|backward)
+xref,r,h,INDEF,,,X input origin in reference units
+yref,r,h,INDEF,,,Y input origin in reference units
+xmag,r,h,INDEF,,,X scale in output units per reference unit
+ymag,r,h,INDEF,,,Y scale in output units per reference unit
+xrotation,r,h,INDEF,,,X axis rotation in degrees
+yrotation,r,h,INDEF,,,Y axis rotation in degrees
+xout,r,h,INDEF,,,X output origin in output units
+yout,r,h,INDEF,,,Y output origin in output units
+xshift,r,h,INDEF,,,X origin shift in output units
+yshift,r,h,INDEF,,,Y origin shift in output units
+
+xcolumn,i,h,1,1,100,Input column containing the x coordinate
+ycolumn,i,h,2,1,100,Input column containing the y coordinate
+calctype,s,h,"real",|real|double|,,Data type for evaluation coordinates
+xformat,s,h,"",,,Output format of the x coordinate
+yformat,s,h,"",,,Output format of the y coordinate
+min_sigdigits,i,h,7,,,Minimum precision of output x and y coordinates
+
+mode,s,h,'ql'
diff --git a/pkg/images/immatch/gregister.cl b/pkg/images/immatch/gregister.cl
new file mode 100644
index 00000000..70c048e7
--- /dev/null
+++ b/pkg/images/immatch/gregister.cl
@@ -0,0 +1,51 @@
+# GREGISTER -- Register a list of images by calling the GEOTRAN task with the
+# appropriate parameters.
+
+procedure gregister (input, output, database, transforms, geometry, xmin, xmax,
+ ymin, ymax, xscale, yscale, ncols, nlines, xsample, ysample,
+ interpolant, boundary, constant, fluxconserve, nxblock, nyblock,
+ verbose)
+
+string input
+string output
+string database
+string transforms
+string geometry
+real xmin
+real xmax
+real ymin
+real ymax
+real xscale
+real yscale
+int ncols
+int nlines
+real xsample
+real ysample
+string interpolant
+string boundary
+real constant
+bool fluxconserve
+int nxblock
+int nyblock
+bool verbose
+
+begin
+ # Declare local variables
+ string din, dout, ddata, dtran
+
+ # Get the parameters.
+ din = input
+ dout = output
+ ddata = database
+ dtran = transforms
+
+ # Call GEOTRAN.
+ geotran (input=din, output=dout, database=ddata, transforms=dtran,
+ geometry=geometry, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax,
+ xscale=xscale, yscale=yscale, ncols=ncols, nlines=nlines,
+ interpolant=interpolant, boundary=boundary, constant=constant,
+ fluxconserve=fluxconserve, xsample=xsample, ysample=ysample,
+ nxblock=nxblock, nyblock=nyblock, xin=INDEF, yin=INDEF, xout=INDEF,
+ yout=INDEF, xshift=INDEF, yshift=INDEF, xmag=INDEF, ymag=INDEF,
+ xrotation=INDEF, yrotation=INDEF, verbose=verbose)
+end
diff --git a/pkg/images/immatch/gregister.par b/pkg/images/immatch/gregister.par
new file mode 100644
index 00000000..ad2e28b3
--- /dev/null
+++ b/pkg/images/immatch/gregister.par
@@ -0,0 +1,33 @@
+# GREGISTER Parameters
+
+# required parameters
+input,f,a,,,,Input data
+output,f,a,,,,Output data
+database,f,a,,,,Name of GEOMAP database file
+transforms,s,a,,,,Names of coordinate transforms in database file
+geometry,s,h,'geometric',,,'Geometry (linear,distortion,geometric)'
+
+# output picture format parameters
+xmin,r,h,INDEF,,,Minimum reference x value of output picture
+xmax,r,h,INDEF,,,Maximum reference x value of output picture
+ymin,r,h,INDEF,,,Minimum reference y value of output picture
+ymax,r,h,INDEF,,,Maximum reference y value of output picture
+xscale,r,h,1.0,0.,,X scale of output picture in reference units per pixel
+yscale,r,h,1.0,0.,,Y scale of output picture in reference units per pixel
+ncols,i,h,INDEF,1,,Number of columns in the output picture
+nlines,i,h,INDEF,1,,Number of lines in the output picture
+
+# coordinate surface and image interpolation parameters
+xsample,r,h,1.0,1.0,,Coordinate surface sampling area in x
+ysample,r,h,1.0,1.0,,Coordinate surface sampling area in y
+interpolant,s,h,'linear',,,'Interpolant (nearest,linear,poly3,poly5,spline3)'
+boundary,s,h,'nearest',|nearest|constant|reflect|wrap|,,'Boundary extension (nearest,constant,reflect,wrap)'
+constant,r,h,0.,,,Constant for constant boundary extension
+fluxconserve,b,h,yes,,,Preserve image flux ?
+
+# blocking factors
+nxblock,i,h,512,,,X dimension of working block size in pixels
+nyblock,i,h,512,,,Y dimension of working block size in pixels
+verbose,b,h,yes,,,Print messages about the progress of the task ?
+
+mode,s,h,'ql'
diff --git a/pkg/images/immatch/imalign.cl b/pkg/images/immatch/imalign.cl
new file mode 100644
index 00000000..2e6a5f69
--- /dev/null
+++ b/pkg/images/immatch/imalign.cl
@@ -0,0 +1,119 @@
+# IMALIGN - Register a set of images using the results of the IMCENTROID,
+# and the IMSHIFT, and IMCOPY tasks.
+
+procedure imalign (input, reference, coords, output)
+
+begin
+ bool shifts_found, trim_found
+ string tmpfile, outfile, shiftfile, trimsect, tmp, junk
+ string l_input, l_reference, l_coords, l_output
+ int x1, x2, y1, y2
+ real xshift, yshift
+ struct line
+
+ # Set up some temporary files.
+ tmpfile = mktemp ("tmp$ia_tmp.")
+ outfile = mktemp ("tmp$ia_tmp.")
+ shiftfile = mktemp ("tmp$ia_tmp.")
+
+ # Get the required parameters.
+ l_input = input
+ l_reference = reference
+ l_coords = coords
+ l_output = output
+
+ # Write the output names to outfile.
+ sections (l_output, option="fullname", > outfile)
+
+ # Compute the centers and relative shifts.
+ imcentroid (l_input, l_reference, l_coords, shifts=shifts,
+ boxsize=boxsize, bigbox=bigbox, negative=negative,
+ background=background, lower=lower, upper=upper,
+ niterate=niterate, tolerance=tolerance, maxshift=maxshift,
+ verbose=verbose, >& tmpfile)
+
+ # Print the centering results on the screen?
+ if (verbose)
+ type (tmpfile)
+
+ # Shift the images.
+ if (shiftimages) {
+
+ # Read the shifts.
+ shifts_found = no
+ list = tmpfile
+ while (fscan (list, line) != EOF) {
+ tmp = substr (line, 2, 7)
+ if (tmp == "Shifts") {
+ shifts_found = yes
+ break
+ }
+ }
+
+ # Decode the shifts.
+ if (shifts_found)
+ while (fscan (list, junk, xshift, junk, yshift, junk) == 5)
+ print (xshift, " ", yshift, >> shiftfile)
+ else
+ error (1, "No shifts were calculated.")
+
+ # Shift the images.
+ print ("\n# Shifting images:\n")
+ imshift (l_input, "@"//outfile, shifts_file=shiftfile,
+ interp_type=interp_type, boundary_type=boundary_type,
+ constant=constant)
+
+ # Trim the images.
+ if (trimimages) {
+
+ # Check for vignetting.
+ trim_found = no
+ while (fscanf (list, "%s = [%d:%d,%d:%d]", line, x1, x2,
+ y1, y2) != EOF) {
+ tmp = substr (line, 2, 5)
+ if (tmp == "Vign") {
+ print ("Images not trimmed ! Vignetting is present.")
+ trim_found = no
+ break
+ } else if (tmp == "Trim") {
+ trim_found = yes
+ break
+ }
+ }
+
+ # Trim the images.
+ if (!trim_found) {
+ print ("Images not trimmed ! Trim section is undefined.")
+ } else {
+
+ # Correct for boundary extension "contamination".
+ if (interp_type == "poly3") {
+ x1 += 1; x2 -= 1; y1 += 1; y2 -= 1
+ } else if (interp_type == "poly5" ||
+ interp_type == "spline3") {
+ x1 += 2; x2 -= 2; y1 += 2; y2 -= 2
+ }
+
+ if (1 <= x1 && x1 <= x2 && 1 <= y1 && y1 <= y2) {
+ trimsect = "["//x1//":"//x2//","//y1//":"//y2//"]"
+
+ list = outfile; delete (tmpfile, ver-, >& "dev$null")
+ while (fscan (list, tmp) != EOF)
+ print (tmp//trimsect, >> tmpfile)
+
+ print ("# Trimming images: corrected section = ",
+ trimsect)
+ imcopy ("@"//tmpfile, "@"//outfile, verbose-)
+
+ } else {
+ print ("Images not trimmed ! No overlap region.")
+ }
+ }
+ }
+ }
+
+ list = ""
+ delete (tmpfile, ver-, >& "dev$null")
+ delete (outfile, ver-, >& "dev$null")
+ delete (shiftfile, ver-, >& "dev$null")
+end
diff --git a/pkg/images/immatch/imalign.par b/pkg/images/immatch/imalign.par
new file mode 100644
index 00000000..2434f735
--- /dev/null
+++ b/pkg/images/immatch/imalign.par
@@ -0,0 +1,28 @@
+input,s,a,,,,"Input images"
+reference,s,a,,,,"Reference image"
+coords,s,a,,,,"Reference coordinates file"
+output,s,a,,,,"Output images"
+shifts,s,h,"",,,"Initial shifts file"
+
+boxsize,i,h,7,1,,"Size of the small centering box"
+bigbox,i,h,11,1,,"Size of the big centering box"
+negative,b,h,no,,,"Are the features negative ?"
+background,r,h,INDEF,,,"Reference background level"
+lower,r,h,INDEF,,,"Lower threshold for data"
+upper,r,h,INDEF,,,"Upper threshold for data"
+niterate,i,h,3,2,,"Maximum number of iterations"
+tolerance,i,h,0,0,,"Tolerance for convergence"
+maxshift,r,h,INDEF,,,"Maximum acceptable pixel shift"
+
+shiftimages,b,h,yes,,,"Shift the images ?"
+interp_type,s,h,"linear","|nearest|linear|poly3|poly5|spline3|",,"Interpolant"
+boundary_type,s,h,"nearest","|constant|nearest|reflect|wrap|",,"Boundary type"
+constant,r,h,0.,,,"Constant for constant boundary extension"
+
+trimimages,b,h,yes,,,"Trim the shifted images ?"
+
+verbose,b,h,yes,,,"Print the centers, shifts, and trim section ?"
+
+list,*s,h
+
+mode,s,h,'ql'
diff --git a/pkg/images/immatch/imcentroid.par b/pkg/images/immatch/imcentroid.par
new file mode 100644
index 00000000..8a93d787
--- /dev/null
+++ b/pkg/images/immatch/imcentroid.par
@@ -0,0 +1,16 @@
+input,s,a,,,,"List of input images"
+reference,s,a,"",,,"Reference image"
+coords,s,a,,,,"Reference coordinates file"
+shifts,s,h,"",,,"Initial shifts file"
+
+boxsize,i,h,7,1,,"Size of the fine centering box"
+bigbox,i,h,11,1,,"Size of the coarse centering box"
+
+negative,b,h,no,,,"Are the features negative ?"
+background,r,h,INDEF,,,"Reference background level"
+lower,r,h,INDEF,,,"Lower threshold for data"
+upper,r,h,INDEF,,,"Upper threshold for data"
+niterate,i,h,3,2,,"Maximum number of iterations"
+tolerance,i,h,0,0,,"Tolerance for convergence"
+maxshift,r,h,INDEF,,,"Maximum acceptable pixel shift"
+verbose,b,h,yes,,,"Print the centroids for every source ?"
diff --git a/pkg/images/immatch/imcombine.par b/pkg/images/immatch/imcombine.par
new file mode 100644
index 00000000..ead908e4
--- /dev/null
+++ b/pkg/images/immatch/imcombine.par
@@ -0,0 +1,43 @@
+# IMCOMBINE -- Image combine parameters
+
+input,s,a,,,,List of images to combine
+output,s,a,,,,List of output images
+headers,s,h,"",,,List of header files (optional)
+bpmasks,s,h,"",,,List of bad pixel masks (optional)
+rejmasks,s,h,"",,,List of rejection masks (optional)
+nrejmasks,s,h,"",,,List of number rejected masks (optional)
+expmasks,s,h,"",,,List of exposure masks (optional)
+sigmas,s,h,"",,,List of sigma images (optional)
+imcmb,s,h,"$I",,,Keyword for IMCMB keywords
+logfile,s,h,"STDOUT",,,"Log file
+"
+combine,s,h,"average","average|median|lmedian|sum|quadrature|nmodel",,Type of combine operation
+reject,s,h,"none","none|minmax|ccdclip|crreject|sigclip|avsigclip|pclip",,Type of rejection
+project,b,h,no,,,Project highest dimension of input images?
+outtype,s,h,"real","short|ushort|integer|long|real|double",,Output image pixel datatype
+outlimits,s,h,"",,,Output limits (x1 x2 y1 y2 ...)
+offsets,f,h,"none",,,Input image offsets
+masktype,s,h,"none","",,Mask type
+maskvalue,s,h,"0",,,Mask value
+blank,r,h,0.,,,"Value if there are no pixels
+"
+scale,s,h,"none",,,Image scaling
+zero,s,h,"none",,,Image zero point offset
+weight,s,h,"none",,,Image weights
+statsec,s,h,"",,,Image section for computing statistics
+expname,s,h,"",,,"Image header exposure time keyword
+"
+lthreshold,r,h,INDEF,,,Lower threshold
+hthreshold,r,h,INDEF,,,Upper threshold
+nlow,i,h,1,0,,minmax: Number of low pixels to reject
+nhigh,i,h,1,0,,minmax: Number of high pixels to reject
+nkeep,i,h,1,,,Minimum to keep (pos) or maximum to reject (neg)
+mclip,b,h,yes,,,Use median in sigma clipping algorithms?
+lsigma,r,h,3.,0.,,Lower sigma clipping factor
+hsigma,r,h,3.,0.,,Upper sigma clipping factor
+rdnoise,s,h,"0.",,,ccdclip: CCD readout noise (electrons)
+gain,s,h,"1.",,,ccdclip: CCD gain (electrons/DN)
+snoise,s,h,"0.",,,ccdclip: Sensitivity noise (fraction)
+sigscale,r,h,0.1,0.,,Tolerance for sigma clipping scaling corrections
+pclip,r,h,-0.5,,,pclip: Percentile clipping parameter
+grow,r,h,0.,0.,,Radius (pixels) for neighbor rejection
diff --git a/pkg/images/immatch/immatch.cl b/pkg/images/immatch/immatch.cl
new file mode 100644
index 00000000..eeecba73
--- /dev/null
+++ b/pkg/images/immatch/immatch.cl
@@ -0,0 +1,39 @@
+#{ IMMATCH -- The Image Matching Package.
+
+set immatch = "images$immatch/"
+set imgeom = "images$imgeom/"
+set imutil = "images$imutil/"
+
+package immatch
+
+# Tasks.
+
+task imcentroid,
+ imcombine,
+ geomap,
+ geotran,
+ geoxytran,
+ linmatch,
+ psfmatch,
+ skyxymatch,
+ wcscopy,
+ wcsxymatch,
+ xregister,
+ xyxymatch = "immatch$x_images.e"
+
+task imshift = "imgeom$x_images.e"
+task imcopy = "imutil$x_images.e"
+task sections = "imutil$x_images.e"
+hidetask imshift, imcopy, sections
+
+# Scripts
+
+task gregister = "immatch$gregister.cl"
+task imalign = "immatch$imalign.cl"
+task skymap = "immatch$skymap.cl"
+task sregister = "immatch$sregister.cl"
+task wcsmap = "immatch$wcsmap.cl"
+task wregister = "immatch$wregister.cl"
+
+
+clbye()
diff --git a/pkg/images/immatch/immatch.hd b/pkg/images/immatch/immatch.hd
new file mode 100644
index 00000000..7478b683
--- /dev/null
+++ b/pkg/images/immatch/immatch.hd
@@ -0,0 +1,32 @@
+# Help directory for the IMMATCH package
+
+$immatch = "images$immatch/"
+$doc = "images$immatch/doc/"
+$geometry = "images$immatch/src/geometry/"
+$imcombine = "images$immatch/src/imcombine/"
+$linmatch = "images$immatch/src/linmatch/"
+$listmatch = "images$immatch/src/listmatch/"
+$psfmatch = "images$immatch/src/psfmatch/"
+$wcsmatch = "images$immatch/src/wcsmatch/"
+$xregister = "images$immatch/src/xregister/"
+
+geomap hlp=doc$geomap.hlp, src=geometry$t_geomap.x
+geotran hlp=doc$geotran.hlp, src=geometry$t_geotran.x
+geoxytran hlp=doc$geoxytran.hlp, src=geometry$t_geoxytran.x
+gregister hlp=doc$gregister.hlp, src=immatch$gregister.cl
+imalign hlp=doc$imalign.hlp, src=immatch$imalign.cl
+imcentroid hlp=doc$imcentroid.hlp, src=listmatch$t_imctroid.x
+imcombine hlp=doc$imcombine.hlp, src=imcombine$t_imcombine.x
+linmatch hlp=doc$linmatch.hlp, src=linmatch$t_linmatch.x
+psfmatch hlp=doc$psfmatch.hlp, src=psfmatch$t_psfmatch.x
+skymap hlp=doc$skymap.hlp, src=immatch$skymap.cl
+skyxymatch hlp=doc$skyxymatch.hlp, src=wcsmatch$t_skyxymatch.x
+sregister hlp=doc$sregister.hlp, src=immatch$sregister.cl
+wcscopy hlp=doc$wcscopy.hlp, src=wcsmatch$t_wcscopy.x
+wcsmap hlp=doc$wcsmap.hlp, src=immatch$wcsmap.cl
+wcsxymatch hlp=doc$wcsxymatch.hlp, src=wcsmatch$t_wcsxymatch.x
+wregister hlp=doc$wregister.hlp, src=immatch$wregister.cl
+xregister hlp=doc$xregister.hlp, src=xregister$t_xregister.x
+xyxymatch hlp=doc$xyxymatch.hlp, src=listmatch$t_xyxymatch.x
+revisions sys=Revisions
+
diff --git a/pkg/images/immatch/immatch.men b/pkg/images/immatch/immatch.men
new file mode 100644
index 00000000..f9829bd7
--- /dev/null
+++ b/pkg/images/immatch/immatch.men
@@ -0,0 +1,18 @@
+ geomap - Compute geometric transforms using matched coordinate lists
+ geotran - Transform 1-D or 2-D images using various mapping transforms
+ geoxytran - Transform coordinate lists using the geomap transforms
+ gregister - Register 1-D or 2-D images using the geomap transforms
+ imalign - Align and register 2-D images using a reference pixel list
+ imcentroid - Compute and print relative shifts for a list of 2-D images
+ imcombine - Combine images pixel-by-pixel using various algorithms
+ linmatch - Match the linear intensity scales of 1-D or 2-D images
+ psfmatch - Match the point-spread functions of 1-D or 2-D images
+ skymap - Compute geometric transforms using the image celestial wcs
+ skyxymatch - Generate matched pixel lists using the image celestial wcs
+ sregister - Register 1-D or 2-D images using the image celestial wcs
+ wcscopy - Copy the wcs from one image to another
+ wcsmap - Compute geometric transforms using the image wcs
+ wcsxymatch - Generate matched pixel lists using the image wcs
+ wregister - Register 1-D or 2-D images using the image wcs
+ xregister - Register 1-D or 2-D images using x-correlation techniques
+ xyxymatch - Match pixel coordinate lists
diff --git a/pkg/images/immatch/immatch.par b/pkg/images/immatch/immatch.par
new file mode 100644
index 00000000..cef3f3ff
--- /dev/null
+++ b/pkg/images/immatch/immatch.par
@@ -0,0 +1 @@
+version,s,h,"Jan97"
diff --git a/pkg/images/immatch/linmatch.par b/pkg/images/immatch/linmatch.par
new file mode 100644
index 00000000..ae3183d5
--- /dev/null
+++ b/pkg/images/immatch/linmatch.par
@@ -0,0 +1,30 @@
+input,s,a,,,,"Input images"
+reference,s,a,,,,"Reference images or reference photometry files"
+regions,s,a,"",,,"Reference image regions or input image photometry files"
+lintransform,f,a,"",,,"Input/output linear transformation database file"
+output,s,h,"",,,"Output scaled images"
+databasefmt,b,h,yes,,,"Write the linear transformatoin file in database format ?"
+append,b,h,yes,,,"Open transformation database for writing in append mode"
+records,s,h,"",,,"List of scale factors database records"
+shifts,f,h,"",,,"Input shifts file"
+xshift,r,h,0.,,,"The input to reference image x shift"
+yshift,r,h,0.,,,"The input to reference image y shift"
+dnx,r,h,31.,,,"X width of data region to extract"
+dny,r,h,31.,,,"Y width of data region to extract"
+maxnregions,i,h,100,,,"Maximum number of regions or objects"
+scaling,s,h,"mean mean",,,"Scaling algorithm (number,mean,median,mode,fit,photometry)"
+datamin,r,h,INDEF,,,"The minimum good data value"
+datamax,r,h,INDEF,,,"The maximum good data value"
+maxiter,i,h,10,,,"Maximum number of least squares fitting iterations"
+nreject,i,h,0,,,"Maximum number of rejection iterations"
+loreject,r,h,INDEF,,,"Low-side fitting sigma rejection criterion"
+hireject,r,h,INDEF,,,"High-side fitting sigma rejection criterion"
+gain,s,h,"1.0 1.0",,,"Image header gain keyword or value"
+readnoise,s,h,"0.0 0.0",,,"Image header readout noise keyword or value"
+interactive,b,h,no,,,"Interactive mode ?"
+verbose,b,h,yes,,,"Verbose mode ?"
+graphics,s,h,"stdgraph",,,"The standard graphics device"
+display,s,h,"stdimage",,,"The standard image display device"
+gcommands,*gcur,h,"",,,"The graphics cursor"
+icommands,*imcur,h,"",,,"The image display cursor"
+mode,s,h,"ql",,,
diff --git a/pkg/images/immatch/mkpkg b/pkg/images/immatch/mkpkg
new file mode 100644
index 00000000..988c7963
--- /dev/null
+++ b/pkg/images/immatch/mkpkg
@@ -0,0 +1,5 @@
+# MKPKG for the IMMATCH Package
+
+libpkg.a:
+ @src
+ ;
diff --git a/pkg/images/immatch/psfmatch.par b/pkg/images/immatch/psfmatch.par
new file mode 100644
index 00000000..042b383b
--- /dev/null
+++ b/pkg/images/immatch/psfmatch.par
@@ -0,0 +1,40 @@
+# The PSFMATCH parameters
+
+input,f,a,,,,Input images
+reference,f,a,,,,Reference images or reference psfs
+psfdata,f,a,,,,Objects lists or input psfs
+kernel,f,a,"",,,Input/output convolution kernels
+output,f,h,"",,,Output convolved images
+
+convolution,s,h,"image","|image|psf|kernel|",,Kernel computation method
+dnx,i,h,31,,,X width of data region to extract
+dny,i,h,31,,,Y width of data region to extract
+pnx,i,h,15,,,X width of convolution kernel
+pny,i,h,15,,,Y width of convolution kernel
+
+center,b,h,"yes",,,Center the psf objects ?
+background,s,h,"median",,,Background fitting function
+loreject,r,h,INDEF,,,Low sigma rejection threshold
+hireject,r,h,INDEF,,,High sigma rejection threshold
+apodize,r,h,0,,,Fraction of endpoints to apodize
+
+fluxratio,s,h,INDEF,,,The reference to input integrated flux ratio
+filter,s,h,"replace","|none|cosbell|replace|model|",,Filter/replace option
+sx1,r,h,INDEF,,,Inner x spectral frequency for cosine bell filter
+sx2,r,h,INDEF,,,Outer x spectral frequency for cosine bell filter
+sy1,r,h,INDEF,,,Inner y spectral frequency for cosine bell filter
+sy2,r,h,INDEF,,,Outer y spectral frequency for cosine bell filter
+radsym,b,h,no,,,Radial symmetry for cosine bell filter ?
+threshold,r,h,0.2,0.0,1.0,Threshold in fourier spectrum for modeling/replacing
+normfactor,r,h,1.0,,,The kernel normalization factor
+
+boundary,s,h,'nearest',"|constant|nearest|reflect|wrap|",,Boundary extension
+constant,r,h,0.0,,,Constant for constant boundary extension
+
+interactive,b,h,no,,,Interactive mode ?
+verbose,b,h,yes,,,Verbose mode ?
+graphics,s,h,"stdgraph",,,The default graphics device
+display,s,h,"stdimage",,,The default display device
+gcommands,*gcur,h,"",,,Graphics cursor
+icommands,*imcur,h,"",,,Image display cursor
+mode,s,h,"ql"
diff --git a/pkg/images/immatch/skymap.cl b/pkg/images/immatch/skymap.cl
new file mode 100644
index 00000000..f8785eb0
--- /dev/null
+++ b/pkg/images/immatch/skymap.cl
@@ -0,0 +1,114 @@
+# SKYMAP -- Compute the geometric transformation required to register an
+# input image to a reference image using celestial coordinate WCS information
+# in the input and reference image headers. SKYMAP is a simple script task
+# which calls the SKYXYMATCH task to compute the control points followed by
+# the GEOMAP task to compute the transformation.
+
+
+procedure skymap (input, reference, database)
+
+file input {prompt="The input images"}
+file reference {prompt="The input reference images"}
+file database {prompt="The output database file"}
+file transforms {"", prompt="The database transform names"}
+file results {"", prompt="The optional results summary files"}
+real xmin {INDEF,
+ prompt="Minimum logical x reference coordinate value"}
+real xmax {INDEF,
+ prompt="Maximum logical x reference coordinate value"}
+real ymin {INDEF,
+ prompt="Minimum logical y reference coordinate value"}
+real ymax {INDEF,
+ prompt="Maximum logical y reference coordinate value"}
+int nx {10, prompt="Number of grid points in x"}
+int ny {10, prompt="Number of grid points in y"}
+string wcs {"world", prompt="The default world coordinate system",
+ enum="physical|world"}
+string xformat {"%10.3f", prompt="Output logical x coordinate format"}
+string yformat {"%10.3f", prompt="Output logical y coordinate format"}
+string rwxformat {"",
+ prompt="Output reference world x coordinate format"}
+string rwyformat {"",
+ prompt="Output reference world y coordinate format"}
+string wxformat {"", prompt="Output world x coordinate format"}
+string wyformat {"", prompt="Output world y coordinate format"}
+string fitgeometry {"general",
+ prompt="Fitting geometry",
+ enum="shift|xyscale|rotate|rscale|rxyscale|general"}
+string function {"polynomial", prompt="Surface type",
+ enum="legendre|chebyshev|polynomial"}
+int xxorder {2, prompt="Order of x fit in x"}
+int xyorder {2, prompt="Order of x fit in y"}
+string xxterms {"half", enum="none|half|full",
+ prompt="X fit cross terms type"}
+int yxorder {2, prompt="Order of y fit in x"}
+int yyorder {2, prompt="Order of y fit in y"}
+string yxterms {"half", enum="none|half|full",
+ prompt="Y fit cross terms type"}
+real reject {INDEF, prompt="Rejection limit in sigma units"}
+string calctype {"real", prompt="Computation precision",
+ enum="real|double"}
+bool verbose {yes, prompt="Print messages about progress of task ?"}
+bool interactive {yes, prompt="Compute transformation interactively ? "}
+string graphics {"stdgraph", prompt="Default graphics device"}
+gcur gcommands {"", prompt="Graphics cursor"}
+
+
+begin
+ # Declare local variables.
+ int nimages
+ string tinput, treference, tcoords, toutput, ttransforms, tresults
+ string tsections1, tsections2, tcname
+
+ # Cache the sections task.
+ cache sections
+
+ # Get the query parameters.
+ tinput = input
+ treference = reference
+ toutput = database
+ if (transforms == "") {
+ ttransforms = tinput
+ } else {
+ ttransforms = transforms
+ }
+ tresults = results
+
+ # Get the temproary coordinates file list.
+ tsections1 = mktemp ("tmps1")
+ tsections2 = mktemp ("tmps2")
+ if (access ("imxymatch.1")) {
+ tcoords = mktemp ("imxymatch")
+ } else {
+ tcoords = "imxymatch"
+ }
+ sections (tinput, option="fullname", > tsections1)
+ nimages = sections.nimages
+ for (i = 1; i <= nimages; i = i + 1) {
+ printf ("%s\n", tcoords // "." // i, >> tsections2)
+ }
+ delete (tsections1, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+ tcname = "@"//tsections2
+
+ # Compute the control points.
+ skyxymatch (tinput, treference, tcname, coords="grid", xmin=xmin,
+ xmax=xmax, ymin=ymin, ymax=ymax, nx=nx, ny=ny, wcs=wcs,
+ xcolumn=1, ycolumn=1, xunits="", yunits="", xformat=xformat,
+ yformat=yformat, rwxformat=rwxformat, rwyformat=rwyformat,
+ wxformat=wxformat, wyformat=wyformat, min_sigdigits=7, verbose=no)
+
+ # Compute the transformation.
+ geomap (tcname, toutput, xmin, xmax, ymin, ymax, transforms=ttransforms,
+ results=tresults, fitgeometry=fitgeometry, function=function,
+ xxorder=xxorder, xyorder=xyorder, xxterms=xxterms, yxorder=yxorder,
+ yyorder=yyorder, yxterms=yxterms, reject=reject, calctype=calctype,
+ verbose=verbose, interactive=interactive, graphics=graphics,
+ cursor=gcommands)
+
+ # Cleanup.
+ delete (tcname, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+ delete (tsections2, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+end
diff --git a/pkg/images/immatch/skyxymatch.par b/pkg/images/immatch/skyxymatch.par
new file mode 100644
index 00000000..7d1f42e1
--- /dev/null
+++ b/pkg/images/immatch/skyxymatch.par
@@ -0,0 +1,26 @@
+# Parameter file for the SKYXYMATCH task
+
+input,f,a,,,,Input images
+reference,f,a,,,,Input reference images
+output,f,a,,,,Output matched coordinate lists
+coords,f,h,"grid",,,Reference coordinate lists
+xmin,r,h,INDEF,,,Minimum logical x reference coordinate value
+xmax,r,h,INDEF,,,Maximum logical x reference coordinate value
+ymin,r,h,INDEF,,,Minimum logical y reference coordinate value
+ymax,r,h,INDEF,,,Maximum logical y reference coordinate value
+nx,i,h,10,1,,Number of grid points in x
+ny,i,h,10,1,,Number of grid points in y
+wcs,s,h,"world","|physical|world|",,Input coordinate system
+xcolumn,i,h,1,1,,Input column containing x coordinate
+ycolumn,i,h,2,1,,Input column containing y coordinate
+xunits,s,h,"",,,Input x coordinate units
+yunits,s,h,"",,,Input y coordinate units
+xformat,s,h,"%10.3f",,,Output logical x coordinate format
+yformat,s,h,"%10.3f",,,Output logical y coordinate format
+rwxformat,s,h,"",,,Output reference world x coordinate format
+rwyformat,s,h,"",,,Output reference world y coordinate format
+wxformat,s,h,"",,,Output world x coordinate format
+wyformat,s,h,"",,,Output world y coordinate format
+min_sigdigits,i,h,7,,,Minimum number of significant digits
+verbose,b,h,yes,,,Verbose mode ?
+mode,s,h,ql,,,
diff --git a/pkg/images/immatch/src/geometry/geofunc.gx b/pkg/images/immatch/src/geometry/geofunc.gx
new file mode 100644
index 00000000..3b34a207
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/geofunc.gx
@@ -0,0 +1,250 @@
+include <math.h>
+include <math/gsurfit.h>
+
+$for (rd)
+
+# GEO_DROTMAG -- Adjust the coefficients of the fit using the database file.
+
+procedure geo_drotmag$t (dt, rec, sx1, sy1, xmag, ymag, xrot, yrot)
+
+pointer dt #I pointer to the text database file
+int rec #I record number
+pointer sx1, sy1 #I/O pointers to the x and y linear surfaces
+PIXEL xmag, ymag #I/O the x and y magnification
+PIXEL xrot, yrot #I/O the x and y axis rotation
+
+real dtgetr()
+
+begin
+ if (IS_$INDEF$T(xmag))
+ xmag = PIXEL (dtgetr (dt, rec, "xmag"))
+ if (IS_$INDEF$T(ymag))
+ ymag = PIXEL (dtgetr (dt, rec, "ymag"))
+ if (IS_$INDEF$T(xrot))
+ xrot = DEGTORAD (PIXEL(dtgetr (dt, rec, "xrotation")))
+ else
+ xrot = DEGTORAD(xrot)
+ if (IS_$INDEF$T(yrot))
+ yrot = DEGTORAD (PIXEL (dtgetr (dt, rec, "yrotation")))
+ else
+ yrot = DEGTORAD(yrot)
+ call geo_rotmag$t (sx1, sy1, xmag, ymag, xrot, yrot)
+end
+
+
+# GEO_DXYSHIFT -- Adjust the fitted xy shift using the database file.
+
+procedure geo_dxyshift$t (dt, rec, sx1, sy1, xout, yout, xref, yref,
+ xshift, yshift)
+
+pointer dt #I pointer to the text file database
+int rec #I the database record
+pointer sx1, sy1 #I/O pointers to the x and y linear surfaces
+PIXEL xout, yout #I the input coordinate system origin
+PIXEL xref, yref #I the reference coordinate system origin
+PIXEL xshift, yshift #I the origin shift in input coordinates
+
+$if (datatype == r)
+PIXEL gsgetr(), gseval()
+$else
+PIXEL dgsgetd(), dgseval()
+$endif
+
+begin
+$if (datatype == r)
+ if (IS_$INDEF$T(xref))
+ xref = (gsgetr (sx1, GSXMIN) + gsgetr (sx1, GSXMAX)) / 2.0
+ if (IS_$INDEF$T(yref))
+ yref = (gsgetr (sy1, GSYMIN) + gsgetr (sy1, GSYMAX)) / 2.0
+
+ if (IS_$INDEF$T(xout))
+ xout = gseval (sx1, xref, yref)
+ if (IS_$INDEF$T(yout))
+ yout = gseval (sy1, xref, yref)
+
+ if (IS_$INDEF$T(xshift))
+ xshift = xout - gseval (sx1, xref, yref)
+ if (IS_$INDEF$T(yshift))
+ yshift = yout - gseval (sy1, xref, yref)
+$else
+ if (IS_$INDEF$T(xref))
+ xref = (dgsgetd (sx1, GSXMIN) + dgsgetd (sx1, GSXMAX)) / 2.0d0
+ if (IS_$INDEF$T(yref))
+ yref = (dgsgetd (sy1, GSYMIN) + dgsgetd (sy1, GSYMAX)) / 2.0d0
+
+ if (IS_$INDEF$T(xout))
+ xout = dgseval (sx1, xref, yref)
+ if (IS_$INDEF$T(yout))
+ yout = dgseval (sy1, xref, yref)
+
+ if (IS_$INDEF$T(xshift))
+ xshift = xout - dgseval (sx1, xref, yref)
+ if (IS_$INDEF$T(yshift))
+ yshift = yout - dgseval (sy1, xref, yref)
+$endif
+
+ call geo_xyshift$t (sx1, sy1, xshift, yshift)
+end
+
+
+# GEO_ROTMAG -- Edit the coefficients of the linear surface which determine
+# magnification and rotation.
+
+procedure geo_rotmag$t (sx1, sy1, xscale, yscale, xrotation, yrotation)
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+PIXEL xscale, yscale #I the x and y scales
+PIXEL xrotation,yrotation #I the x and y axis rotation angles in radians
+
+PIXEL cosx, sinx, cosy, siny, xrange, yrange
+int ncoeff
+pointer sp, xcoeff, ycoeff
+$if (datatype == r)
+real gsgetr()
+int gsgeti()
+$else
+double dgsgetd()
+int dgsgeti()
+$endif
+
+begin
+ # Get the current solution.
+ call smark (sp)
+$if (datatype == r)
+ ncoeff = max (gsgeti (sx1, GSNSAVE), gsgeti (sy1, GSNSAVE))
+$else
+ ncoeff = max (dgsgeti (sx1, GSNSAVE), dgsgeti (sy1, GSNSAVE))
+$endif
+ call salloc (xcoeff, ncoeff, TY_PIXEL)
+ call salloc (ycoeff, ncoeff, TY_PIXEL)
+$if (datatype == r)
+ call gssave (sx1, Mem$t[xcoeff])
+ call gssave (sy1, Mem$t[ycoeff])
+$else
+ call dgssave (sx1, Mem$t[xcoeff])
+ call dgssave (sy1, Mem$t[ycoeff])
+$endif
+
+ # Define the scaling parameters.
+ cosx = cos (xrotation)
+ sinx = sin (xrotation)
+ cosy = cos (yrotation)
+ siny = sin (yrotation)
+
+ # Calculate coefficients.
+ Mem$t[xcoeff+GS_SAVECOEFF+1] = xscale * cosx
+ Mem$t[xcoeff+GS_SAVECOEFF+2] = yscale * siny
+ Mem$t[ycoeff+GS_SAVECOEFF+1] = -xscale * sinx
+ Mem$t[ycoeff+GS_SAVECOEFF+2] = yscale * cosy
+
+ # Normalize coefficients for-non polynomial functions.
+$if (datatype == r)
+ if (gsgeti (sx1, GSTYPE) != GS_POLYNOMIAL) {
+ xrange = gsget$t (sx1, GSXMAX) - gsget$t (sx1, GSXMIN)
+$else
+ if (dgsgeti (sx1, GSTYPE) != GS_POLYNOMIAL) {
+ xrange = dgsget$t (sx1, GSXMAX) - dgsget$t (sx1, GSXMIN)
+$endif
+ Mem$t[xcoeff+GS_SAVECOEFF+1] = Mem$t[xcoeff+GS_SAVECOEFF+1] *
+ xrange / 2.d0
+ Mem$t[xcoeff+GS_SAVECOEFF+2] = Mem$t[xcoeff+GS_SAVECOEFF+2] *
+ yrange / 2.d0
+ }
+$if (datatype == r)
+ if (gsgeti (sy1, GSTYPE) != GS_POLYNOMIAL) {
+ yrange = gsget$t (sy1, GSYMAX) - gsget$t (sy1, GSYMIN)
+$else
+ if (dgsgeti (sy1, GSTYPE) != GS_POLYNOMIAL) {
+ yrange = dgsget$t (sy1, GSYMAX) - dgsget$t (sy1, GSYMIN)
+$endif
+ Mem$t[ycoeff+GS_SAVECOEFF+1] = Mem$t[ycoeff+GS_SAVECOEFF+1] *
+ xrange / 2.d0
+ Mem$t[ycoeff+GS_SAVECOEFF+2] = Mem$t[ycoeff+GS_SAVECOEFF+2] *
+ yrange / 2.d0
+ }
+
+$if (datatype == r)
+ # Free the original fit.
+ call gsfree (sx1)
+ call gsfree (sy1)
+
+ # Restore the edited fit.
+ call gsrestore (sx1, Mem$t[xcoeff])
+ call gsrestore (sy1, Mem$t[ycoeff])
+$else
+ # Free the original fit.
+ call dgsfree (sx1)
+ call dgsfree (sy1)
+
+ # Restore the edited fit.
+ call dgsrestore (sx1, Mem$t[xcoeff])
+ call dgsrestore (sy1, Mem$t[ycoeff])
+$endif
+
+ call sfree (sp)
+end
+
+
+# GEO_XYSHIFT -- Shift the linear part of the fit in x and y.
+
+procedure geo_xyshift$t (sx1, sy1, xshift, yshift)
+
+pointer sx1, sy1 #I pointers to linear x and y surfaces
+PIXEL xshift, yshift #I the input x and y shifts
+
+int ncoeff
+pointer sp, xcoeff, ycoeff
+$if (datatype == r)
+int gsgeti()
+$else
+int dgsgeti()
+$endif
+
+begin
+ call smark (sp)
+
+ # Allocate working space.
+$if (datatype == r)
+ ncoeff = max (gsgeti (sx1, GSNSAVE), gsgeti (sy1, GSNSAVE))
+$else
+ ncoeff = max (dgsgeti (sx1, GSNSAVE), dgsgeti (sy1, GSNSAVE))
+$endif
+ call salloc (xcoeff, ncoeff, TY_PIXEL)
+ call salloc (ycoeff, ncoeff, TY_PIXEL)
+
+ # Get coefficients.
+$if (datatype == r)
+ call gssave (sx1, Mem$t[xcoeff])
+ call gssave (sy1, Mem$t[ycoeff])
+$else
+ call dgssave (sx1, Mem$t[xcoeff])
+ call dgssave (sy1, Mem$t[ycoeff])
+$endif
+
+ # Shift the coefficients.
+ Mem$t[xcoeff+GS_SAVECOEFF] = Mem$t[xcoeff+GS_SAVECOEFF] + xshift
+ Mem$t[ycoeff+GS_SAVECOEFF] = Mem$t[ycoeff+GS_SAVECOEFF] + yshift
+
+$if (datatype == r)
+ # Free original fit.
+ call gsfree (sx1)
+ call gsfree (sy1)
+
+ # Restore fit.
+ call gsrestore (sx1, Mem$t[xcoeff])
+ call gsrestore (sy1, Mem$t[ycoeff])
+$else
+ # Free original fit.
+ call dgsfree (sx1)
+ call dgsfree (sy1)
+
+ # Restore fit.
+ call dgsrestore (sx1, Mem$t[xcoeff])
+ call dgsrestore (sy1, Mem$t[ycoeff])
+$endif
+
+ call sfree (sp)
+end
+
+
+$endfor
diff --git a/pkg/images/immatch/src/geometry/geofunc.x b/pkg/images/immatch/src/geometry/geofunc.x
new file mode 100644
index 00000000..c3be8fa5
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/geofunc.x
@@ -0,0 +1,340 @@
+include <math.h>
+include <math/gsurfit.h>
+
+
+
+# GEO_DROTMAG -- Adjust the coefficients of the fit using the database file.
+
+procedure geo_drotmagr (dt, rec, sx1, sy1, xmag, ymag, xrot, yrot)
+
+pointer dt #I pointer to the text database file
+int rec #I record number
+pointer sx1, sy1 #I/O pointers to the x and y linear surfaces
+real xmag, ymag #I/O the x and y magnification
+real xrot, yrot #I/O the x and y axis rotation
+
+real dtgetr()
+
+begin
+ if (IS_INDEFR(xmag))
+ xmag = real (dtgetr (dt, rec, "xmag"))
+ if (IS_INDEFR(ymag))
+ ymag = real (dtgetr (dt, rec, "ymag"))
+ if (IS_INDEFR(xrot))
+ xrot = DEGTORAD (real(dtgetr (dt, rec, "xrotation")))
+ else
+ xrot = DEGTORAD(xrot)
+ if (IS_INDEFR(yrot))
+ yrot = DEGTORAD (real (dtgetr (dt, rec, "yrotation")))
+ else
+ yrot = DEGTORAD(yrot)
+ call geo_rotmagr (sx1, sy1, xmag, ymag, xrot, yrot)
+end
+
+
+# GEO_DXYSHIFT -- Adjust the fitted xy shift using the database file.
+
+procedure geo_dxyshiftr (dt, rec, sx1, sy1, xout, yout, xref, yref,
+ xshift, yshift)
+
+pointer dt #I pointer to the text file database
+int rec #I the database record
+pointer sx1, sy1 #I/O pointers to the x and y linear surfaces
+real xout, yout #I the input coordinate system origin
+real xref, yref #I the reference coordinate system origin
+real xshift, yshift #I the origin shift in input coordinates
+
+real gsgetr(), gseval()
+
+begin
+ if (IS_INDEFR(xref))
+ xref = (gsgetr (sx1, GSXMIN) + gsgetr (sx1, GSXMAX)) / 2.0
+ if (IS_INDEFR(yref))
+ yref = (gsgetr (sy1, GSYMIN) + gsgetr (sy1, GSYMAX)) / 2.0
+
+ if (IS_INDEFR(xout))
+ xout = gseval (sx1, xref, yref)
+ if (IS_INDEFR(yout))
+ yout = gseval (sy1, xref, yref)
+
+ if (IS_INDEFR(xshift))
+ xshift = xout - gseval (sx1, xref, yref)
+ if (IS_INDEFR(yshift))
+ yshift = yout - gseval (sy1, xref, yref)
+
+ call geo_xyshiftr (sx1, sy1, xshift, yshift)
+end
+
+
+# GEO_ROTMAG -- Edit the coefficients of the linear surface which determine
+# magnification and rotation.
+
+procedure geo_rotmagr (sx1, sy1, xscale, yscale, xrotation, yrotation)
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+real xscale, yscale #I the x and y scales
+real xrotation,yrotation #I the x and y axis rotation angles in radians
+
+real cosx, sinx, cosy, siny, xrange, yrange
+int ncoeff
+pointer sp, xcoeff, ycoeff
+real gsgetr()
+int gsgeti()
+
+begin
+ # Get the current solution.
+ call smark (sp)
+ ncoeff = max (gsgeti (sx1, GSNSAVE), gsgeti (sy1, GSNSAVE))
+ call salloc (xcoeff, ncoeff, TY_REAL)
+ call salloc (ycoeff, ncoeff, TY_REAL)
+ call gssave (sx1, Memr[xcoeff])
+ call gssave (sy1, Memr[ycoeff])
+
+ # Define the scaling parameters.
+ cosx = cos (xrotation)
+ sinx = sin (xrotation)
+ cosy = cos (yrotation)
+ siny = sin (yrotation)
+
+ # Calculate coefficients.
+ Memr[xcoeff+GS_SAVECOEFF+1] = xscale * cosx
+ Memr[xcoeff+GS_SAVECOEFF+2] = yscale * siny
+ Memr[ycoeff+GS_SAVECOEFF+1] = -xscale * sinx
+ Memr[ycoeff+GS_SAVECOEFF+2] = yscale * cosy
+
+ # Normalize coefficients for-non polynomial functions.
+ if (gsgeti (sx1, GSTYPE) != GS_POLYNOMIAL) {
+ xrange = gsgetr (sx1, GSXMAX) - gsgetr (sx1, GSXMIN)
+ Memr[xcoeff+GS_SAVECOEFF+1] = Memr[xcoeff+GS_SAVECOEFF+1] *
+ xrange / 2.d0
+ Memr[xcoeff+GS_SAVECOEFF+2] = Memr[xcoeff+GS_SAVECOEFF+2] *
+ yrange / 2.d0
+ }
+ if (gsgeti (sy1, GSTYPE) != GS_POLYNOMIAL) {
+ yrange = gsgetr (sy1, GSYMAX) - gsgetr (sy1, GSYMIN)
+ Memr[ycoeff+GS_SAVECOEFF+1] = Memr[ycoeff+GS_SAVECOEFF+1] *
+ xrange / 2.d0
+ Memr[ycoeff+GS_SAVECOEFF+2] = Memr[ycoeff+GS_SAVECOEFF+2] *
+ yrange / 2.d0
+ }
+
+ # Free the original fit.
+ call gsfree (sx1)
+ call gsfree (sy1)
+
+ # Restore the edited fit.
+ call gsrestore (sx1, Memr[xcoeff])
+ call gsrestore (sy1, Memr[ycoeff])
+
+ call sfree (sp)
+end
+
+
+# GEO_XYSHIFT -- Shift the linear part of the fit in x and y.
+
+procedure geo_xyshiftr (sx1, sy1, xshift, yshift)
+
+pointer sx1, sy1 #I pointers to linear x and y surfaces
+real xshift, yshift #I the input x and y shifts
+
+int ncoeff
+pointer sp, xcoeff, ycoeff
+int gsgeti()
+
+begin
+ call smark (sp)
+
+ # Allocate working space.
+ ncoeff = max (gsgeti (sx1, GSNSAVE), gsgeti (sy1, GSNSAVE))
+ call salloc (xcoeff, ncoeff, TY_REAL)
+ call salloc (ycoeff, ncoeff, TY_REAL)
+
+ # Get coefficients.
+ call gssave (sx1, Memr[xcoeff])
+ call gssave (sy1, Memr[ycoeff])
+
+ # Shift the coefficients.
+ Memr[xcoeff+GS_SAVECOEFF] = Memr[xcoeff+GS_SAVECOEFF] + xshift
+ Memr[ycoeff+GS_SAVECOEFF] = Memr[ycoeff+GS_SAVECOEFF] + yshift
+
+ # Free original fit.
+ call gsfree (sx1)
+ call gsfree (sy1)
+
+ # Restore fit.
+ call gsrestore (sx1, Memr[xcoeff])
+ call gsrestore (sy1, Memr[ycoeff])
+
+ call sfree (sp)
+end
+
+
+
+
+# GEO_DROTMAG -- Adjust the coefficients of the fit using the database file.
+
+procedure geo_drotmagd (dt, rec, sx1, sy1, xmag, ymag, xrot, yrot)
+
+pointer dt #I pointer to the text database file
+int rec #I record number
+pointer sx1, sy1 #I/O pointers to the x and y linear surfaces
+double xmag, ymag #I/O the x and y magnification
+double xrot, yrot #I/O the x and y axis rotation
+
+real dtgetr()
+
+begin
+ if (IS_INDEFD(xmag))
+ xmag = double (dtgetr (dt, rec, "xmag"))
+ if (IS_INDEFD(ymag))
+ ymag = double (dtgetr (dt, rec, "ymag"))
+ if (IS_INDEFD(xrot))
+ xrot = DEGTORAD (double(dtgetr (dt, rec, "xrotation")))
+ else
+ xrot = DEGTORAD(xrot)
+ if (IS_INDEFD(yrot))
+ yrot = DEGTORAD (double (dtgetr (dt, rec, "yrotation")))
+ else
+ yrot = DEGTORAD(yrot)
+ call geo_rotmagd (sx1, sy1, xmag, ymag, xrot, yrot)
+end
+
+
+# GEO_DXYSHIFT -- Adjust the fitted xy shift using the database file.
+
+procedure geo_dxyshiftd (dt, rec, sx1, sy1, xout, yout, xref, yref,
+ xshift, yshift)
+
+pointer dt #I pointer to the text file database
+int rec #I the database record
+pointer sx1, sy1 #I/O pointers to the x and y linear surfaces
+double xout, yout #I the input coordinate system origin
+double xref, yref #I the reference coordinate system origin
+double xshift, yshift #I the origin shift in input coordinates
+
+double dgsgetd(), dgseval()
+
+begin
+ if (IS_INDEFD(xref))
+ xref = (dgsgetd (sx1, GSXMIN) + dgsgetd (sx1, GSXMAX)) / 2.0d0
+ if (IS_INDEFD(yref))
+ yref = (dgsgetd (sy1, GSYMIN) + dgsgetd (sy1, GSYMAX)) / 2.0d0
+
+ if (IS_INDEFD(xout))
+ xout = dgseval (sx1, xref, yref)
+ if (IS_INDEFD(yout))
+ yout = dgseval (sy1, xref, yref)
+
+ if (IS_INDEFD(xshift))
+ xshift = xout - dgseval (sx1, xref, yref)
+ if (IS_INDEFD(yshift))
+ yshift = yout - dgseval (sy1, xref, yref)
+
+ call geo_xyshiftd (sx1, sy1, xshift, yshift)
+end
+
+
+# GEO_ROTMAG -- Edit the coefficients of the linear surface which determine
+# magnification and rotation.
+
+procedure geo_rotmagd (sx1, sy1, xscale, yscale, xrotation, yrotation)
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+double xscale, yscale #I the x and y scales
+double xrotation,yrotation #I the x and y axis rotation angles in radians
+
+double cosx, sinx, cosy, siny, xrange, yrange
+int ncoeff
+pointer sp, xcoeff, ycoeff
+double dgsgetd()
+int dgsgeti()
+
+begin
+ # Get the current solution.
+ call smark (sp)
+ ncoeff = max (dgsgeti (sx1, GSNSAVE), dgsgeti (sy1, GSNSAVE))
+ call salloc (xcoeff, ncoeff, TY_DOUBLE)
+ call salloc (ycoeff, ncoeff, TY_DOUBLE)
+ call dgssave (sx1, Memd[xcoeff])
+ call dgssave (sy1, Memd[ycoeff])
+
+ # Define the scaling parameters.
+ cosx = cos (xrotation)
+ sinx = sin (xrotation)
+ cosy = cos (yrotation)
+ siny = sin (yrotation)
+
+ # Calculate coefficients.
+ Memd[xcoeff+GS_SAVECOEFF+1] = xscale * cosx
+ Memd[xcoeff+GS_SAVECOEFF+2] = yscale * siny
+ Memd[ycoeff+GS_SAVECOEFF+1] = -xscale * sinx
+ Memd[ycoeff+GS_SAVECOEFF+2] = yscale * cosy
+
+ # Normalize coefficients for-non polynomial functions.
+ if (dgsgeti (sx1, GSTYPE) != GS_POLYNOMIAL) {
+ xrange = dgsgetd (sx1, GSXMAX) - dgsgetd (sx1, GSXMIN)
+ Memd[xcoeff+GS_SAVECOEFF+1] = Memd[xcoeff+GS_SAVECOEFF+1] *
+ xrange / 2.d0
+ Memd[xcoeff+GS_SAVECOEFF+2] = Memd[xcoeff+GS_SAVECOEFF+2] *
+ yrange / 2.d0
+ }
+ if (dgsgeti (sy1, GSTYPE) != GS_POLYNOMIAL) {
+ yrange = dgsgetd (sy1, GSYMAX) - dgsgetd (sy1, GSYMIN)
+ Memd[ycoeff+GS_SAVECOEFF+1] = Memd[ycoeff+GS_SAVECOEFF+1] *
+ xrange / 2.d0
+ Memd[ycoeff+GS_SAVECOEFF+2] = Memd[ycoeff+GS_SAVECOEFF+2] *
+ yrange / 2.d0
+ }
+
+ # Free the original fit.
+ call dgsfree (sx1)
+ call dgsfree (sy1)
+
+ # Restore the edited fit.
+ call dgsrestore (sx1, Memd[xcoeff])
+ call dgsrestore (sy1, Memd[ycoeff])
+
+ call sfree (sp)
+end
+
+
+# GEO_XYSHIFT -- Shift the linear part of the fit in x and y.
+
+procedure geo_xyshiftd (sx1, sy1, xshift, yshift)
+
+pointer sx1, sy1 #I pointers to linear x and y surfaces
+double xshift, yshift #I the input x and y shifts
+
+int ncoeff
+pointer sp, xcoeff, ycoeff
+int dgsgeti()
+
+begin
+ call smark (sp)
+
+ # Allocate working space.
+ ncoeff = max (dgsgeti (sx1, GSNSAVE), dgsgeti (sy1, GSNSAVE))
+ call salloc (xcoeff, ncoeff, TY_DOUBLE)
+ call salloc (ycoeff, ncoeff, TY_DOUBLE)
+
+ # Get coefficients.
+ call dgssave (sx1, Memd[xcoeff])
+ call dgssave (sy1, Memd[ycoeff])
+
+ # Shift the coefficients.
+ Memd[xcoeff+GS_SAVECOEFF] = Memd[xcoeff+GS_SAVECOEFF] + xshift
+ Memd[ycoeff+GS_SAVECOEFF] = Memd[ycoeff+GS_SAVECOEFF] + yshift
+
+ # Free original fit.
+ call dgsfree (sx1)
+ call dgsfree (sy1)
+
+ # Restore fit.
+ call dgsrestore (sx1, Memd[xcoeff])
+ call dgsrestore (sy1, Memd[ycoeff])
+
+ call sfree (sp)
+end
+
+
+
diff --git a/pkg/images/immatch/src/geometry/geotimtran.x b/pkg/images/immatch/src/geometry/geotimtran.x
new file mode 100644
index 00000000..f84a794d
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/geotimtran.x
@@ -0,0 +1,543 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include <mach.h>
+include <math/gsurfit.h>
+include <math/iminterp.h>
+include "geotran.h"
+
+# GEO_IMTRAN -- Correct an entire image for geometric distortion using the
+# transformed coordinates and image interpolation.
+
+procedure geo_imtran (input, output, geo, sx1, sy1, sx2, sy2)
+
+pointer input #I pointer to input image
+pointer output #I pointer to output image
+pointer geo #I pointer to geotran structure
+pointer sx1, sy1 #I pointers to linear surface descriptors
+pointer sx2, sy2 #I pointer to higher order surface descriptors
+
+int nincr
+pointer sp, xref, yref, msi
+real shift
+real gsgetr()
+
+begin
+ # Initialize the interpolant and compute the out-of-bounds pixel
+ # margin required.
+ if (IM_NDIM(input) == 1) {
+ call asitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo),
+ GT_NSINC(geo), nincr, shift)
+ call asisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo),
+ nincr, shift, 0.0)
+ } else {
+ call msitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo),
+ GT_NSINC(geo), nincr, shift)
+ call msisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo),
+ nincr, nincr, shift, shift, 0.0)
+ }
+ call geo_margset (sx1, sy1, sx2, sy2, GT_XMIN(geo), GT_XMAX(geo),
+ GT_NCOLS(geo), GT_YMIN(geo), GT_YMAX(geo), GT_NLINES(geo),
+ GT_INTERPOLANT(geo), GT_NSINC(geo), GT_NXYMARGIN(geo))
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (xref, GT_NCOLS(geo), TY_REAL)
+ call salloc (yref, GT_NLINES(geo), TY_REAL)
+
+ # Calculate the reference coordinates of the input image pixels.
+ call geo_ref (geo, Memr[xref], 1, GT_NCOLS(geo), GT_NCOLS(geo),
+ Memr[yref], 1, GT_NLINES(geo), GT_NLINES(geo), gsgetr (sx1,
+ GSXMIN), gsgetr (sx1, GSXMAX), gsgetr (sx1, GSYMIN), gsgetr (sx1,
+ GSYMAX), GT_ONE)
+
+ # Configure the out-of-bounds pixel references for the input image.
+ call geo_imset (input, geo, sx1, sy1, sx2, sy2, Memr[xref],
+ GT_NCOLS(geo), Memr[yref], GT_NLINES(geo))
+
+ # Interpolate.
+ call geo_gsvector (input, output, geo, msi, Memr[xref], 1,
+ GT_NCOLS(geo), Memr[yref], 1, GT_NLINES(geo), sx1, sy1, sx2, sy2)
+
+ # Clean up.
+ if (IM_NDIM(input) == 1)
+ call asifree (msi)
+ else
+ call msifree (msi)
+ call sfree (sp)
+end
+
+
+# GEO_SIMTRAN -- Correct an entire image for geometric distortion using
+# nterpolated coordinate surfaces to speed up computation of the transformed
+# coordinates and image interpolation.
+
+procedure geo_simtran (input, output, geo, sx1, sy1, sx2, sy2)
+
+pointer input #I pointer to input image
+pointer output #I pointer to output image
+pointer geo #I pointer to geotran structure
+pointer sx1, sy1 #I pointer to linear surface descriptors
+pointer sx2, sy2 #I pointer to higher order surface descriptors
+
+int nxsample, nysample, nincr
+pointer sp, xsample, ysample, xinterp, yinterp
+pointer xmsi, ymsi, jmsi, msi, xbuf, ybuf, jbuf
+real shift
+real gsgetr()
+
+begin
+ # Allocate working space and intialize the interpolant.
+ call smark (sp)
+ call salloc (xsample, GT_NCOLS(geo), TY_REAL)
+ call salloc (ysample, GT_NLINES(geo), TY_REAL)
+ call salloc (xinterp, GT_NCOLS(geo), TY_REAL)
+ call salloc (yinterp, GT_NLINES(geo), TY_REAL)
+
+ # Set up sampling size.
+ if (GT_NCOLS(geo) == 1)
+ nxsample = 1
+ else
+ nxsample = GT_NCOLS(geo) / GT_XSAMPLE(geo)
+ if (GT_NLINES(geo) == 1)
+ nysample = 1
+ else
+ nysample = GT_NLINES(geo) / GT_YSAMPLE(geo)
+
+ # Initialize interpolants.
+ if (IM_NDIM(input) == 1) {
+ call asiinit (xmsi, II_LINEAR)
+ call asiinit (ymsi, II_LINEAR)
+ call asitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo),
+ GT_NSINC(geo), nincr, shift)
+ call asisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo),
+ nincr, shift, 0.0)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call asiinit (jmsi, II_LINEAR)
+ } else {
+ call msiinit (xmsi, II_BILINEAR)
+ call msiinit (ymsi, II_BILINEAR)
+ call msitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo),
+ GT_NSINC(geo), nincr, shift)
+ call msisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo),
+ nincr, nincr, shift, shift, 0.0)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call msiinit (jmsi, II_BILINEAR)
+ }
+ call geo_margset (sx1, sy1, sx2, sy2, GT_XMIN(geo), GT_XMAX(geo),
+ GT_NCOLS(geo), GT_YMIN(geo), GT_YMAX(geo), GT_NLINES(geo),
+ GT_INTERPOLANT(geo), GT_NSINC(geo), GT_NXYMARGIN(geo))
+
+ # Setup input image boundary extension parameters.
+ call geo_ref (geo, Memr[xsample], 1, GT_NCOLS(geo), GT_NCOLS(geo),
+ Memr[ysample], 1, GT_NLINES(geo), GT_NLINES(geo), gsgetr (sx1,
+ GSXMIN), gsgetr (sx1, GSXMAX), gsgetr (sx1, GSYMIN), gsgetr (sx1,
+ GSYMAX), GT_ONE)
+ call geo_imset (input, geo, sx1, sy1, sx2, sy2, Memr[xsample],
+ GT_NCOLS(geo), Memr[ysample], GT_NLINES(geo))
+
+ # Calculate the sampled reference coordinates and the interpolated
+ # reference coordinates.
+ call geo_ref (geo, Memr[xsample], 1, nxsample, nxsample, Memr[ysample],
+ 1, nysample, nysample, gsgetr (sx1, GSXMIN), gsgetr (sx1, GSXMAX),
+ gsgetr (sx1, GSYMIN), gsgetr (sx1, GSYMAX), GT_ONE)
+ call geo_sample (geo, Memr[xinterp], 1, GT_NCOLS(geo), nxsample,
+ Memr[yinterp], 1, GT_NLINES(geo), nysample, GT_ONE)
+
+ # Initialize the buffers
+ xbuf = NULL
+ ybuf = NULL
+ jbuf = NULL
+
+ # Set up interpolants
+ call geo_xbuffer (sx1, sx2, xmsi, Memr[xsample], Memr[ysample], 1,
+ nxsample, 1, nysample, xbuf)
+ call geo_ybuffer (sy1, sy2, ymsi, Memr[xsample], Memr[ysample], 1,
+ nxsample, 1, nysample, ybuf)
+ if (GT_FLUXCONSERVE(geo) == YES && (sx2 != NULL || sy2 != NULL)) {
+ if (IM_NDIM(input) == 1)
+ call geo_jbuffer (sx1, NULL, sx2, NULL, jmsi, Memr[xsample],
+ Memr[ysample], 1, nxsample, 1, nysample, jbuf)
+ else
+ call geo_jbuffer (sx1, sy1, sx2, sy2, jmsi, Memr[xsample],
+ Memr[ysample], 1, nxsample, 1, nysample, jbuf)
+ }
+
+ # Transform the image.
+ call geo_msivector (input, output, geo, xmsi, ymsi, jmsi, msi,
+ sx1, sy1, sx2, sy2, Memr[xinterp], 1, GT_NCOLS(geo), nxsample,
+ Memr[yinterp], 1, GT_NLINES(geo), nysample, 1, 1)
+
+ # Free space.
+ if (IM_NDIM(input) == 1) {
+ call asifree (xmsi)
+ call asifree (ymsi)
+ call asifree (msi)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call asifree (jmsi)
+ } else {
+ call msifree (xmsi)
+ call msifree (ymsi)
+ call msifree (msi)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call msifree (jmsi)
+ }
+ call mfree (xbuf, TY_REAL)
+ call mfree (ybuf, TY_REAL)
+ if (jbuf != NULL)
+ call mfree (jbuf, TY_REAL)
+ call sfree (sp)
+end
+
+
+## GEO_IMSIVECTOR -- Evaluate the output image using interpolated surface
+## coordinates.
+#
+#procedure geo_imsivector (in, out, geo, xmsi, ymsi, jmsi, msi, sx1, sy1, sx2,
+# sy2, xref, yref, ncols, nlines)
+#
+#pointer in #I pointer to input image
+#pointer out #I pointer to output image
+#pointer geo #I pointer to geotran structure
+#pointer xmsi, ymsi #I pointer to the interpolation xy surfaces
+#pointer jmsi #I pointer to Jacobian surface
+#pointer msi #I pointer to interpolation surface
+#pointer sx1, sy1 #I linear surface descriptors
+#pointer sx2, sy2 #I distortion surface pointers
+#real xref[ARB] #I x reference coordinates
+#real yref[ARB] #I y reference coordinates
+#int ncols, nlines #I number of columns and rows
+#
+#int j
+#pointer sp, x, y, xin, yin, xout, yout, inbuf, outbuf
+#real factor
+#pointer imgs1r(), imgs2r(), imps1r(), imps2r()
+#real geo_jfactor()
+#
+#begin
+# # Allocate working space.
+# call smark (sp)
+# call salloc (x, ncols, TY_REAL)
+# call salloc (y, ncols, TY_REAL)
+# call salloc (xin, ncols, TY_REAL)
+# call salloc (yin, ncols, TY_REAL)
+# call salloc (xout, ncols, TY_REAL)
+# call salloc (yout, ncols, TY_REAL)
+#
+# # Fit the interpolant
+# if (IM_NDIM(in) == 1)
+# inbuf = imgs1r (in, 1, int (IM_LEN(in,1)))
+# else
+# inbuf = imgs2r (in, 1, int (IM_LEN(in,1)), 1, int (IM_LEN(in,2)))
+# if (inbuf == EOF)
+# call error (0, "Error reading image")
+# if (IM_NDIM(in) == 1)
+# call asifit (msi, Memr[inbuf], int (IM_LEN(in,1)))
+# else
+# call msifit (msi, Memr[inbuf], int (IM_LEN(in,1)),
+# int (IM_LEN(in,2)), int (IM_LEN(in,1)))
+#
+# # Compute the output bufferr.
+# do j = 1, nlines {
+#
+# # Compute coordinates.
+# call amovkr (yref[j], Memr[y], ncols)
+# if (IM_NDIM(in) == 1) {
+# call asivector (xmsi, xref, Memr[xin], ncols)
+# call asivector (ymsi, xref, Memr[yin], ncols)
+# } else {
+# call msivector (xmsi, xref, Memr[y], Memr[xin], ncols)
+# call msivector (ymsi, xref, Memr[y], Memr[yin], ncols)
+# }
+#
+# # Correct for out-of-bounds pixels.
+# call geo_btran (in, geo, Memr[xin], Memr[yin], Memr[xout],
+# Memr[yout], ncols)
+#
+# # Write to output image.
+# if (IM_NDIM(in) == 1)
+# outbuf = imps1r (out, 1, ncols)
+# else
+# outbuf = imps2r (out, 1, ncols, j, j)
+# if (outbuf == EOF)
+# call error (0, "Error writing output image")
+# if (IM_NDIM(in) == 1)
+# call asivector (msi, Memr[xout], Memr[outbuf], ncols)
+# else
+# call msivector (msi, Memr[xout], Memr[yout], Memr[outbuf],
+# ncols)
+#
+# # Perform constant boundary extension.
+# if (GT_BOUNDARY(geo) == BT_CONSTANT)
+# call geo_bconstant (in, geo, Memr[xin], Memr[yin],
+# Memr[outbuf], Memr[outbuf], ncols)
+#
+# # Preserve flux in image.
+# if (GT_FLUXCONSERVE(geo) == YES) {
+# factor = GT_XSCALE(geo) * GT_YSCALE(geo)
+# if (GT_GEOMODE(geo) == GT_LINEAR || (sx2 == NULL && sy2 ==
+# NULL)) {
+# if (IM_NDIM(in) == 1)
+# call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+# NULL), Memr[outbuf], ncols)
+# else
+# call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+# sy1), Memr[outbuf], ncols)
+# } else {
+# if (IM_NDIM(in) == 1)
+# call geo_msiflux (jmsi, xref, yref, Memr[outbuf],
+# 1, ncols, 0, 1, 1)
+# else
+# call geo_msiflux (jmsi, xref, yref, Memr[outbuf],
+# 1, ncols, j, 1, 1)
+# call amulkr (Memr[outbuf], factor, Memr[outbuf], ncols)
+# }
+# }
+# }
+#
+# call sfree (sp)
+#end
+
+
+## GEO_IGSVECTOR -- Evaluate the output image using fitted coordinates.
+#
+#procedure geo_igsvector (input, output, geo, msi, xref, yref, ncols, nlines,
+# sx1, sy1, sx2, sy2)
+#
+#pointer input #I pointer to input image
+#pointer output #I pointer to output image
+#pointer geo #I pointer to geotran structure
+#pointer msi #I pointer to interpolant
+#real xref[ARB] #I x reference array
+#real yref[ARB] #I y reference array
+#int ncols, nlines #I number of columns and lines
+#pointer sx1, sy1 #I pointer to linear surface
+#pointer sx2, sy2 #I pointer to distortion surface
+#
+#int j
+#pointer sp, y, xin, yin, xout, yout, temp, inbuf, outbuf
+#real factor
+#pointer imgs1r(), imgs2r(), imps1r(), imps2r()
+#real geo_jfactor()
+#
+#begin
+# # Allocate working space.
+# call smark (sp)
+# call salloc (y, ncols, TY_REAL)
+# call salloc (xin, ncols, TY_REAL)
+# call salloc (yin, ncols, TY_REAL)
+# call salloc (xout, ncols, TY_REAL)
+# call salloc (yout, ncols, TY_REAL)
+# call salloc (temp, ncols, TY_REAL)
+#
+# # Fill image buffer.
+# if (IM_NDIM(input) == 1)
+# inbuf = imgs1r (input, 1, int (IM_LEN(input,1)))
+# else
+# inbuf = imgs2r (input, 1, int (IM_LEN(input,1)), 1,
+# int (IM_LEN(input,2)))
+# if (inbuf == EOF)
+# call error (0, "Error reading image")
+#
+# # Fit the interpolant.
+# if (IM_NDIM(input) == 1)
+# call asifit (msi, Memr[inbuf], int (IM_LEN(input,1)))
+# else
+# call msifit (msi, Memr[inbuf], int (IM_LEN(input,1)),
+# int (IM_LEN(input,2)), int (IM_LEN(input,1)))
+#
+# # Calculate the x and y input image coordinates.
+# do j = 1, nlines {
+#
+# # Get output image buffer.
+# if (IM_NDIM(input) == 1)
+# outbuf = imps1r (output, 1, ncols)
+# else
+# outbuf = imps2r (output, 1, ncols, j, j)
+# if (output == EOF)
+# call error (0, "Error writing output image")
+#
+# # Fit x coords.
+# call amovkr (yref[j], Memr[y], ncols)
+# call gsvector (sx1, xref, Memr[y], Memr[xin], ncols)
+# if (sx2 != NULL) {
+# call gsvector (sx2, xref, Memr[y], Memr[temp], ncols)
+# call aaddr (Memr[xin], Memr[temp], Memr[xin], ncols)
+# }
+#
+# # Fit y coords.
+# call gsvector (sy1, xref, Memr[y], Memr[yin], ncols)
+# if (sy2 != NULL) {
+# call gsvector (sy2, xref, Memr[y], Memr[temp], ncols)
+# call aaddr (Memr[yin], Memr[temp], Memr[yin], ncols)
+# }
+#
+# # Compute of of bounds pixels.
+# call geo_btran (input, geo, Memr[xin], Memr[yin], Memr[xout],
+# Memr[yout], ncols)
+#
+# # Interpolate in input image.
+# if (IM_NDIM(input) == 1)
+# call asivector (msi, Memr[xout], Memr[outbuf], ncols)
+# else
+# call msivector (msi, Memr[xout], Memr[yout], Memr[outbuf],
+# ncols)
+#
+# # Correct for constant boundary extension.
+# if (GT_BOUNDARY(geo) == BT_CONSTANT)
+# call geo_bconstant (input, geo, Memr[xin], Memr[yin],
+# Memr[outbuf], Memr[outbuf], ncols)
+#
+# # Preserve flux in image.
+# if (GT_FLUXCONSERVE(geo) == YES) {
+# factor = GT_XSCALE(geo) * GT_YSCALE(geo)
+# if (GT_GEOMODE(geo) == GT_LINEAR || (sx2 == NULL && sy2 ==
+# NULL)) {
+# if (IM_NDIM(input) == 1)
+# call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+# NULL), Memr[outbuf], ncols)
+# else
+# call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+# sy1), Memr[outbuf], ncols)
+# } else {
+# if (IM_NDIM(input) == 1)
+# call geo_gsflux (xref, yref, Memr[outbuf], 1, ncols, j,
+# sx1, NULL, sx2, NULL)
+# else
+# call geo_gsflux (xref, yref, Memr[outbuf], 1, ncols, j,
+# sx1, sy1, sx2, sy2)
+# call amulkr (Memr[outbuf], factor, Memr[outbuf], ncols)
+# }
+# }
+# }
+#
+# call sfree (sp)
+#end
+
+
+## GEO_BTRAN -- Map out-of-bounds pixel into the input image.
+#
+#procedure geo_btran (input, geo, xin, yin, xout, yout, ncols)
+#
+#pointer input #I pointer to the input image
+#pointer geo #I pointer to geotran strcuture
+#real xin[ARB] #I x input coords
+#real yin[ARB] #I y input coords
+#real xout[ARB] #O x output coords
+#real yout[ARB] #O y output coords
+#int ncols #I number of columns
+#
+#int i
+#real xmax, ymax, xtemp, ytemp
+#
+#begin
+# xmax = IM_LEN(input,1)
+# if (IM_NDIM(input) == 1)
+# ymax = 1.0
+# else
+# ymax = IM_LEN(input,2)
+#
+# switch (GT_BOUNDARY(geo)) {
+# case BT_CONSTANT, BT_NEAREST:
+# do i = 1, ncols {
+# if (xin[i] < 1.0)
+# xout[i] = 1.0
+# else if (xin[i] > xmax)
+# xout[i] = xmax
+# else
+# xout[i] = xin[i]
+# if (yin[i] < 1.0)
+# yout[i] = 1.0
+# else if (yin[i] > ymax)
+# yout[i] = ymax
+# else
+# yout[i] = yin[i]
+# }
+# case BT_REFLECT:
+# do i = 1, ncols {
+# if (xin[i] < 1.0)
+# xout[i] = 1.0 + (1.0 - xin[i])
+# else if (xin[i] > xmax)
+# xout[i] = xmax - (xin[i] - xmax)
+# else
+# xout[i] = xin[i]
+# if (yin[i] < 1.0)
+# yout[i] = 1.0 + (1.0 - yin[i])
+# else if (yin[i] > ymax)
+# yout[i] = ymax - (yin[i] - ymax)
+# else
+# yout[i] = yin[i]
+# }
+# case BT_WRAP:
+# do i = 1, ncols {
+# xtemp = xin[i]
+# ytemp = yin[i]
+#
+# if (xtemp < 1.0) {
+# while (xtemp < 1.0)
+# xtemp = xtemp + xmax
+# if (xtemp < 1.0)
+# xtemp = xmax - xtemp
+# else if (xtemp > xmax)
+# xtemp = 2.0 + xmax - xtemp
+# } else if (xtemp > xmax) {
+# while (xtemp > xmax)
+# xtemp = xtemp - xmax
+# if (xtemp < 1.0)
+# xtemp = xmax - xtemp
+# else if (xtemp > xmax)
+# xtemp = 2.0 + xmax - xtemp
+# }
+# xout[i] = xtemp
+#
+# if (ytemp < 1.0) {
+# while (ytemp < 1.0)
+# ytemp = ytemp + ymax
+# if (ytemp < 1.0)
+# ytemp = ymax - ytemp
+# else if (ytemp > ymax)
+# ytemp = 2.0 + ymax - ytemp
+# } else if (ytemp > ymax) {
+# while (ytemp > ymax)
+# ytemp = ytemp - ymax
+# if (ytemp < 1.0)
+# ytemp = ymax - ytemp
+# else if (ytemp > ymax)
+# ytemp = 2.0 + ymax - ytemp
+# }
+# yout[i] = ytemp
+# }
+# }
+#end
+
+
+## GEO_BCONSTANT -- Map constant out-of-bounds pixels into the input image.
+#
+#procedure geo_bconstant (input, geo, xin, yin, inbuf, outbuf, ncols)
+#
+#pointer input #I pointer to the input image
+#pointer geo #I pointer to geotran structure
+#real xin[ARB] #I x input coords
+#real yin[ARB] #I y input coords
+#real inbuf[ARB] #I input buffer
+#real outbuf[ARB] #O output buffer
+#int ncols #I number of columns
+#
+#int i
+#real xmax, ymax, constant
+#
+#begin
+# xmax = IM_LEN(input,1)
+# if (IM_NDIM(input) == 1)
+# ymax = 1.0
+# else
+# ymax = IM_LEN(input,2)
+# constant = GT_CONSTANT(geo)
+# do i = 1, ncols {
+# if (xin[i] < 1.0 || xin[i] > xmax || yin[i] < 1.0 || yin[i] > ymax)
+# outbuf[i] = constant
+# else
+# outbuf[i] = inbuf[i]
+# }
+#end
diff --git a/pkg/images/immatch/src/geometry/geotran.h b/pkg/images/immatch/src/geometry/geotran.h
new file mode 100644
index 00000000..d2fa6b55
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/geotran.h
@@ -0,0 +1,52 @@
+# GEOTRAN Structure
+
+define LEN_GEOSTRUCT (30 + SZ_FNAME)
+
+# output picture formatting parameters
+
+define GT_NCOLS Memi[$1] # number of output columns
+define GT_NLINES Memi[$1+1] # number of output lines
+define GT_XMIN Memr[P2R($1+2)] # x minimum
+define GT_XMAX Memr[P2R($1+3)] # x maximum
+define GT_YMIN Memr[P2R($1+4)] # y minimun
+define GT_YMAX Memr[P2R($1+5)] # y maximum
+define GT_XSCALE Memr[P2R($1+6)] # x scale
+define GT_YSCALE Memr[P2R($1+7)] # y scale
+
+# transformation parameters
+
+define GT_GEOMODE Memi[$1+8] # Type of transformation
+define GT_XIN Memr[P2R($1+9)] # x input pixel
+define GT_YIN Memr[P2R($1+10)] # y input pixel
+define GT_XOUT Memr[P2R($1+11)] # x output pixel
+define GT_YOUT Memr[P2R($1+12)] # y output pixel
+define GT_XSHIFT Memr[P2R($1+13)] # x shift
+define GT_YSHIFT Memr[P2R($1+14)] # y shift
+define GT_XMAG Memr[P2R($1+15)] # input image x scale
+define GT_YMAG Memr[P2R($1+16)] # input image y scale
+define GT_XROTATION Memr[P2R($1+17)] # rotation angle
+define GT_YROTATION Memr[P2R($1+18)] # scale angle
+
+# interpolation parameters
+define GT_XSAMPLE Memr[P2R($1+19)] # x surface subsampling
+define GT_YSAMPLE Memr[P2R($1+20)] # y surface subsampling
+define GT_INTERPOLANT Memi[$1+21] # image interpolant
+define GT_NSINC Memi[$1+22] # sinc width half-width
+define GT_NXYMARGIN Memi[$1+23] # the interpolation margin
+define GT_BOUNDARY Memi[$1+24] # boundary extension
+define GT_CONSTANT Memr[P2R($1+25)] # constant boundary extension
+define GT_FLUXCONSERVE Memi[$1+26] # conserve total flux
+define GT_INTERPSTR Memc[P2C($1+27)] # interpolation string
+
+# GEOTRAN MODES
+
+define GT_NONE 1 # parameters defined by user
+define GT_LINEAR 2 # use linear transformation
+define GT_DISTORT 3 # distortion transformation only
+define GT_GEOMETRIC 4 # use full transformation
+
+# GEOTRAN COORDINATE MODES
+
+define GT_ONE 1
+define GT_TWO 2
+define GT_FOUR 3
diff --git a/pkg/images/immatch/src/geometry/geotran.x b/pkg/images/immatch/src/geometry/geotran.x
new file mode 100644
index 00000000..ee689d26
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/geotran.x
@@ -0,0 +1,1752 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include <mach.h>
+include <math/gsurfit.h>
+include <math/iminterp.h>
+include "geotran.h"
+
+define NMARGIN 3 # number of boundary pixels
+define NMARGIN_SPLINE3 16 # number of spline boundary pixels
+
+# GEO_TRAN -- Correct an image for geometric distortion block by block using
+# fitted coordinates and image interpolation.
+
+procedure geo_tran (input, output, geo, sx1, sy1, sx2, sy2, nxblock, nyblock)
+
+pointer input #I pointer to input image
+pointer output #I pointer to output image
+pointer geo #I pointer to geotran structure
+pointer sx1, sy1 #I pointers to linear surfaces
+pointer sx2, sy2 #I pointers to higher order surfaces
+int nxblock, nyblock #I working block size
+
+int l1, l2, c1, c2, nincr
+pointer sp, xref, yref, msi
+real shift
+real gsgetr()
+
+begin
+ # Initialize the interpolant.
+ if (IM_NDIM(input) == 1) {
+ call asitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo), GT_NSINC(geo),
+ nincr, shift)
+ call asisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo), nincr,
+ shift, 0.0)
+ } else {
+ call msitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo),
+ GT_NSINC(geo), nincr, shift)
+ call msisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo), nincr,
+ nincr, shift, shift, 0.0)
+ }
+ call geo_margset (sx1, sy1, sx2, sy2, GT_XMIN(geo), GT_XMAX(geo),
+ GT_NCOLS(geo), GT_YMIN(geo), GT_YMAX(geo), GT_NLINES(geo),
+ GT_INTERPOLANT(geo), GT_NSINC(geo), GT_NXYMARGIN(geo))
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (xref, GT_NCOLS(geo), TY_REAL)
+ call salloc (yref, GT_NLINES(geo), TY_REAL)
+
+ # Compute the reference coordinates corresponding to the center of
+ # the output image pixels.
+ call geo_ref (geo, Memr[xref], 1, GT_NCOLS(geo), GT_NCOLS(geo),
+ Memr[yref], 1, GT_NLINES(geo), GT_NLINES(geo), gsgetr (sx1,
+ GSXMIN), gsgetr (sx1, GSXMAX), gsgetr (sx1, GSYMIN), gsgetr (sx1,
+ GSYMAX), GT_ONE)
+
+ # Configure the out-of-bounds pixel references for the input image.
+ call geo_imset (input, geo, sx1, sy1, sx2, sy2, Memr[xref],
+ GT_NCOLS(geo), Memr[yref], GT_NLINES(geo))
+
+ # Loop over the line blocks.
+ for (l1 = 1; l1 <= GT_NLINES(geo); l1 = l1 + nyblock) {
+
+ # Set line limits in the output image.
+ l2 = min (l1 + nyblock - 1, GT_NLINES(geo))
+
+ # Loop over the column blocks
+ for (c1 = 1; c1 <= GT_NCOLS(geo); c1 = c1 + nxblock) {
+
+ # Set column limits in the output image.
+ c2 = min (c1 + nxblock - 1, GT_NCOLS(geo))
+
+ # Interpolate
+ call geo_gsvector (input, output, geo, msi, Memr[xref],
+ c1, c2, Memr[yref], l1, l2, sx1, sy1, sx2, sy2)
+ }
+ }
+
+ # Clean up.
+ if (IM_NDIM(input) == 1)
+ call asifree (msi)
+ else
+ call msifree (msi)
+ call sfree (sp)
+end
+
+
+# GEO_STRAN -- Correct an image for geometric distortion block by block using
+# interpolated coordinates and image interpolation.
+
+procedure geo_stran (input, output, geo, sx1, sy1, sx2, sy2, nxblock, nyblock)
+
+pointer input #I pointer to input image
+pointer output #I pointer to output image
+pointer geo #I pointer to geotran structure
+pointer sx1, sy1 #I pointers to linear surfaces
+pointer sx2, sy2 #I pointers to higher order surfaces
+int nxblock, nyblock #I working block size
+
+int nxsample, nysample, ncols, nlines, l1, l2, c1, c2
+int line1, line2, llast1, llast2, nincr
+pointer sp, xsample, ysample, xinterp, yinterp
+pointer xmsi, ymsi, jmsi, msi, xbuf, ybuf, jbuf
+real shift
+real gsgetr()
+
+begin
+ # Allocate working space and intialize the interpolant.
+ call smark (sp)
+ call salloc (xsample, GT_NCOLS(geo), TY_REAL)
+ call salloc (ysample, GT_NLINES(geo), TY_REAL)
+ call salloc (xinterp, GT_NCOLS(geo), TY_REAL)
+ call salloc (yinterp, GT_NLINES(geo), TY_REAL)
+
+ # Compute the sample size.
+ if (GT_NCOLS(geo) == 1)
+ nxsample = 1
+ else
+ nxsample = GT_NCOLS(geo) / GT_XSAMPLE(geo)
+ if (GT_NLINES(geo) == 1)
+ nysample = 1
+ else
+ nysample = GT_NLINES(geo) / GT_YSAMPLE(geo)
+
+ # Initialize interpolants.
+ if (IM_NDIM(input) == 1) {
+ call asiinit (xmsi, II_LINEAR)
+ call asiinit (ymsi, II_LINEAR)
+ call asitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo),
+ GT_NSINC(geo), nincr, shift)
+ call asisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo), nincr,
+ shift, 0.0)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call asiinit (jmsi, II_LINEAR)
+ } else {
+ call msiinit (xmsi, II_BILINEAR)
+ call msiinit (ymsi, II_BILINEAR)
+ call msitype (GT_INTERPSTR(geo), GT_INTERPOLANT(geo),
+ GT_NSINC(geo), nincr, shift)
+ call msisinit (msi, GT_INTERPOLANT(geo), GT_NSINC(geo), nincr,
+ nincr, shift, shift, 0.0)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call msiinit (jmsi, II_BILINEAR)
+ }
+ call geo_margset (sx1, sy1, sx2, sy2, GT_XMIN(geo), GT_XMAX(geo),
+ GT_NCOLS(geo), GT_YMIN(geo), GT_YMAX(geo), GT_NLINES(geo),
+ GT_INTERPOLANT(geo), GT_NSINC(geo), GT_NXYMARGIN(geo))
+
+ # Setup input image boundary extension parameters.
+ call geo_ref (geo, Memr[xsample], 1, GT_NCOLS(geo), GT_NCOLS(geo),
+ Memr[ysample], 1, GT_NLINES(geo), GT_NLINES(geo), gsgetr (sx1,
+ GSXMIN), gsgetr (sx1, GSXMAX), gsgetr (sx1, GSYMIN), gsgetr (sx1,
+ GSYMAX), GT_ONE)
+ call geo_imset (input, geo, sx1, sy1, sx2, sy2, Memr[xsample],
+ GT_NCOLS(geo), Memr[ysample], GT_NLINES(geo))
+
+ # Calculate the sampled reference coordinates and the interpolated
+ # reference coordinates.
+ call geo_ref (geo, Memr[xsample], 1, nxsample, nxsample, Memr[ysample],
+ 1, nysample, nysample, gsgetr (sx1, GSXMIN), gsgetr (sx1, GSXMAX),
+ gsgetr (sx1, GSYMIN), gsgetr (sx1, GSYMAX), GT_ONE)
+ call geo_sample (geo, Memr[xinterp], 1, GT_NCOLS(geo), nxsample,
+ Memr[yinterp], 1, GT_NLINES(geo), nysample, GT_ONE)
+
+ # Initialize the buffers.
+ xbuf = NULL
+ ybuf = NULL
+ jbuf = NULL
+
+ # Loop over the line blocks.
+ for (l1 = 1; l1 <= GT_NLINES(geo); l1 = l1 + nyblock) {
+
+ # Set line limits in the output image.
+ l2 = min (l1 + nyblock - 1, GT_NLINES(geo))
+ nlines = l2 - l1 + 1
+
+ # Line1 and line2 are the coordinates in the interpolation surface
+ line1 = max (1, min (nysample - 1, int (Memr[yinterp+l1-1])))
+ line2 = min (nysample, int (Memr[yinterp+l2-1] + 1.0))
+
+ if ((xbuf == NULL) || (ybuf == NULL) || (jbuf == NULL) ||
+ (line1 < llast1) || (line2 > llast2)) {
+ call geo_xbuffer (sx1, sx2, xmsi, Memr[xsample], Memr[ysample],
+ 1, nxsample, line1, line2, xbuf)
+ call geo_ybuffer (sy1, sy2, ymsi, Memr[xsample], Memr[ysample],
+ 1, nxsample, line1, line2, ybuf)
+ if (GT_FLUXCONSERVE(geo) == YES) {
+ if (IM_NDIM(input) == 1)
+ call geo_jbuffer (sx1, NULL, sx2, NULL, jmsi,
+ Memr[xsample], Memr[ysample], 1, nxsample,
+ line1, line2, jbuf)
+ else
+ call geo_jbuffer (sx1, sy1, sx2, sy2, jmsi,
+ Memr[xsample], Memr[ysample], 1, nxsample,
+ line1, line2, jbuf)
+ }
+ llast1 = line1
+ llast2 = line2
+ }
+
+
+ # Loop over the column blocks.
+ for (c1 = 1; c1 <= GT_NCOLS(geo); c1 = c1 + nxblock) {
+
+ # C1 and c2 are the column limits in the output image.
+ c2 = min (c1 + nxblock - 1, GT_NCOLS(geo))
+ ncols = c2 - c1 + 1
+
+ # Calculate the coordinates of the output pixels in the input
+ # image.
+ call geo_msivector (input, output, geo, xmsi, ymsi, jmsi, msi,
+ sx1, sy1, sx2, sy2, Memr[xinterp], c1, c2, nxsample,
+ Memr[yinterp], l1, l2, nysample, 1, line1)
+ }
+ }
+
+ # Free space.
+ if (IM_NDIM(input) == 1) {
+ call asifree (xmsi)
+ call asifree (ymsi)
+ call asifree (msi)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call asifree (jmsi)
+ } else {
+ call msifree (xmsi)
+ call msifree (ymsi)
+ call msifree (msi)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call msifree (jmsi)
+ }
+ call mfree (xbuf, TY_REAL)
+ call mfree (ybuf, TY_REAL)
+ if (GT_FLUXCONSERVE(geo) == YES)
+ call mfree (jbuf, TY_REAL)
+ call sfree (sp)
+end
+
+
+# GEO_REF -- Determine the x and y coordinates at which the coordinate
+# surface will be subsampled.
+
+procedure geo_ref (geo, x, c1, c2, nx, y, l1, l2, ny, xmin, xmax, ymin, ymax,
+ cmode)
+
+pointer geo #I pointer to the geotran structure
+real x[ARB] #O output x sample coordinates
+int c1, c2, nx #I the column limits of the sampled array
+real y[ARB] #O output y sample coordinates
+int l1, l2, ny #I the line limits of the output coordinates
+real xmin, xmax #I limits on x coordinates
+real ymin, ymax #I limits on y coordinates
+int cmode #I coordinate computation mode
+
+int i
+real xtempmin, xtempmax, ytempmin, ytempmax, dx, dy
+
+begin
+
+ switch (cmode) {
+ case GT_FOUR:
+ if (nx == 1) {
+ xtempmin = min (xmax, max (xmin, GT_XMIN(geo)))
+ xtempmax = min (xmax, max (xmin, GT_XMAX(geo)))
+ x[1] = xtempmin
+ x[2] = xtempmax
+ x[3] = xtempmax
+ x[4] = xtempmin
+ } else if (nx == GT_NCOLS(geo)) {
+ if (GT_XMIN(geo) > GT_XMAX(geo))
+ dx = -GT_XSCALE(geo)
+ else
+ dx = GT_XSCALE(geo)
+ do i = c1, c2 {
+ xtempmin = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 1.5) * dx))
+ xtempmax = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 0.5) * dx))
+ x[4*(i-c1)+1] = xtempmin
+ x[4*(i-c1)+2] = xtempmax
+ x[4*(i-c1)+3] = xtempmax
+ x[4*(i-c1)+4] = xtempmin
+ }
+ } else {
+ if (GT_XMIN(geo) > GT_XMAX(geo))
+ dx = -GT_XSCALE(geo) * (GT_NCOLS(geo) - 1.0) / (nx - 1.0)
+ else
+ dx = GT_XSCALE(geo) * (GT_NCOLS(geo) - 1.0) / (nx - 1.0)
+ do i = c1, c2 {
+ xtempmin = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 1.5) * dx))
+ xtempmax = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 0.5) * dx))
+ x[4*(i-c1)+1] = xtempmin
+ x[4*(i-c1)+2] = xtempmax
+ x[4*(i-c1)+3] = xtempmax
+ x[4*(i-c1)+4] = xtempmin
+ }
+ }
+
+ case GT_TWO:
+ if (nx == 1) {
+ xtempmin = min (xmax, max (xmin, GT_XMIN(geo)))
+ xtempmax = min (xmax, max (xmin, GT_XMAX(geo)))
+ x[1] = xtempmin
+ x[2] = xtempmax
+ } else if (nx == GT_NCOLS(geo)) {
+ if (GT_XMIN(geo) > GT_XMAX(geo))
+ dx = -GT_XSCALE(geo)
+ else
+ dx = GT_XSCALE(geo)
+ do i = c1, c2 {
+ xtempmin = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 1.5) * dx))
+ xtempmax = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 0.5) * dx))
+ x[2*(i-c1)+1] = xtempmin
+ x[2*(i-c1)+2] = xtempmax
+ }
+ } else {
+ if (GT_XMIN(geo) > GT_XMAX(geo))
+ dx = -GT_XSCALE(geo) * (GT_NCOLS(geo) - 1.0) / (nx - 1.0)
+ else
+ dx = GT_XSCALE(geo) * (GT_NCOLS(geo) - 1.0) / (nx - 1.0)
+ do i = c1, c2 {
+ xtempmin = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 1.5) * dx))
+ xtempmax = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 0.5) * dx))
+ x[2*(i-c1)+1] = xtempmin
+ x[2*(i-c1)+2] = xtempmax
+ }
+ }
+
+ case GT_ONE:
+ if (nx == 1) {
+ x[1] = min (xmax, max (xmin,
+ (GT_XMIN(geo) + GT_XMAX(geo)) / 2.0))
+ } else if (nx == GT_NCOLS(geo)) {
+ if (GT_XMIN(geo) > GT_XMAX(geo))
+ dx = -GT_XSCALE(geo)
+ else
+ dx = GT_XSCALE(geo)
+ do i = c1, c2
+ x[i-c1+1] = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 1) * dx))
+ } else {
+ if (GT_XMIN(geo) > GT_XMAX(geo))
+ dx = -GT_XSCALE(geo) * (GT_NCOLS(geo) - 1.0) / (nx - 1.0)
+ else
+ dx = GT_XSCALE(geo) * (GT_NCOLS(geo) - 1.0) / (nx - 1.0)
+ do i = c1, c2
+ x[i-c1+1] = min (xmax, max (xmin, GT_XMIN(geo) +
+ (i - 1) * dx))
+ }
+
+ }
+
+ switch (cmode) {
+ case GT_FOUR:
+ if (ny == 1) {
+ ytempmin = min (ymax, max (ymin, GT_YMIN(geo)))
+ ytempmax = min (ymax, max (ymin, GT_YMAX(geo)))
+ y[1] = ytempmin
+ y[2] = ytempmin
+ y[3] = ytempmax
+ y[4] = ytempmax
+ } else if (ny == GT_NLINES(geo)) {
+ if (GT_YMIN(geo) > GT_YMAX(geo))
+ dy = -GT_YSCALE(geo)
+ else
+ dy = GT_YSCALE(geo)
+ do i = l1, l2 {
+ ytempmin = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 1.5) * dy))
+ ytempmax = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 0.5) * dy))
+ y[4*(i-l1)+1] = ytempmin
+ y[4*(i-l1)+2] = ytempmin
+ y[4*(i-l1)+3] = ytempmax
+ y[4*(i-l1)+4] = ytempmax
+ }
+ } else {
+ if (GT_YMIN(geo) > GT_YMAX(geo))
+ dy = -GT_YSCALE(geo) * (GT_NLINES(geo) - 1.0) / (ny - 1.0)
+ else
+ dy = GT_YSCALE(geo) * (GT_NLINES(geo) - 1.0) / (ny - 1.0)
+ do i = l1, l2 {
+ ytempmin = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 1.5) * dy))
+ ytempmax = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 0.5) * dy))
+ y[4*(i-l1)+1] = ytempmin
+ y[4*(i-l1)+2] = ytempmin
+ y[4*(i-l1)+3] = ytempmax
+ y[4*(i-l1)+4] = ytempmax
+ }
+ }
+
+ case GT_TWO:
+ if (ny == 1) {
+ ytempmin = min (ymax, max (ymin, GT_YMIN(geo)))
+ ytempmax = min (ymax, max (ymin, GT_YMAX(geo)))
+ y[1] = ytempmin
+ y[2] = ytempmax
+ } else if (ny == GT_NLINES(geo)) {
+ if (GT_YMIN(geo) > GT_YMAX(geo))
+ dy = -GT_YSCALE(geo)
+ else
+ dy = GT_YSCALE(geo)
+ do i = l1, l2 {
+ ytempmin = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 1.5) * dy))
+ ytempmax = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 0.5) * dy))
+ y[2*(i-l1)+1] = ytempmin
+ y[2*(i-l1)+2] = ytempmax
+ }
+ } else {
+ if (GT_YMIN(geo) > GT_YMAX(geo))
+ dy = -GT_YSCALE(geo) * (GT_NLINES(geo) - 1.0) / (ny - 1.0)
+ else
+ dy = GT_YSCALE(geo) * (GT_NLINES(geo) - 1.0) / (ny - 1.0)
+ do i = l1, l2 {
+ ytempmin = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 1.5) * dy))
+ ytempmax = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 0.5) * dy))
+ y[2*(i-l1)+1] = ytempmin
+ y[2*(i-l1)+2] = ytempmax
+ }
+ }
+ case GT_ONE:
+ if (ny == 1) {
+ y[1] = min (ymax, max (ymin,
+ (GT_YMIN(geo) + GT_YMAX(geo)) / 2.0))
+ } else if (ny == GT_NLINES(geo)) {
+ if (GT_YMIN(geo) > GT_YMAX(geo))
+ dy = -GT_YSCALE(geo)
+ else
+ dy = GT_YSCALE(geo)
+ do i = l1, l2
+ y[i-l1+1] = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 1) * dy))
+ } else {
+ if (GT_YMIN(geo) > GT_YMAX(geo))
+ dy = -GT_YSCALE(geo) * (GT_NLINES(geo) - 1.0) / (ny - 1.0)
+ else
+ dy = GT_YSCALE(geo) * (GT_NLINES(geo) - 1.0) / (ny - 1.0)
+ do i = l1, l2
+ y[i-l1+1] = min (ymax, max (ymin, GT_YMIN(geo) +
+ (i - 1) * dy))
+ }
+
+ }
+end
+
+
+# GEO_SAMPLE -- Calculate the sampled reference points.
+
+procedure geo_sample (geo, xref, c1, c2, nxsample, yref, l1, l2, nysample,
+ cmode)
+
+pointer geo #I pointer to geotran structure
+real xref[ARB] #O x reference values
+int c1, c2, nxsample #I limits and number of sample points in x
+real yref[ARB] #O y reference values
+int l1, l2, nysample #I limits and number of sample points in y
+int cmode #I coordinate computation mode
+
+int i
+real xtempmin, xtempmax, ytempmin, ytempmax
+
+begin
+ switch (cmode) {
+ case GT_FOUR:
+ if (GT_NCOLS(geo) == 1) {
+ xref[1] = 0.5
+ xref[2] = 1.5
+ xref[3] = 1.5
+ xref[4] = 0.5
+ } else {
+ do i = c1, c2 {
+ xtempmin = min (real (nxsample), max (1.,
+ real ((nxsample - 1) * (i - 0.5) + (GT_NCOLS(geo) -
+ nxsample)) / (GT_NCOLS(geo) - 1)))
+ xtempmax = min (real (nxsample), max (1.,
+ real ((nxsample - 1) * (i + 0.5) + (GT_NCOLS(geo) -
+ nxsample)) / (GT_NCOLS(geo) - 1)))
+ xref[4*(i-c1)+1] = xtempmin
+ xref[4*(i-c1)+2] = xtempmax
+ xref[4*(i-c1)+3] = xtempmax
+ xref[4*(i-c1)+4] = xtempmin
+ }
+
+ }
+ case GT_TWO:
+ if (GT_NCOLS(geo) == 1) {
+ xref[1] = 0.5
+ xref[2] = 1.5
+ } else {
+ do i = c1, c2 {
+ xtempmin = min (real (nxsample), max (1.,
+ real ((nxsample - 1) * (i - 0.5) + (GT_NCOLS(geo) -
+ nxsample)) / (GT_NCOLS(geo) - 1)))
+ xtempmax = min (real (nxsample), max (1.,
+ real ((nxsample - 1) * (i + 0.5) + (GT_NCOLS(geo) -
+ nxsample)) / (GT_NCOLS(geo) - 1)))
+ xref[2*(i-c1)+1] = xtempmin
+ xref[2*(i-c1)+2] = xtempmax
+ }
+ }
+ case GT_ONE:
+ if (GT_NCOLS(geo) == 1)
+ xref[1] = 1.0
+ else {
+ do i = c1, c2
+ xref[i-c1+1] = min (real (nxsample), max (1.,
+ real ((nxsample - 1) * i + (GT_NCOLS(geo) -
+ nxsample)) / (GT_NCOLS(geo) - 1)))
+ }
+ }
+
+ switch (cmode) {
+ case GT_FOUR:
+ if (GT_NLINES(geo) == 1) {
+ yref[1] = 0.5
+ yref[2] = 0.5
+ yref[3] = 1.5
+ yref[4] = 1.5
+ } else {
+ do i = l1, l2 {
+ ytempmin = min (real (nysample), max (1.,
+ real ((nysample - 1) * (i - 0.5) + (GT_NLINES(geo) -
+ nysample)) / (GT_NLINES(geo) - 1)))
+ ytempmax = min (real (nysample), max (1.,
+ real ((nysample - 1) * (i + 0.5) + (GT_NLINES(geo) -
+ nysample)) / (GT_NLINES(geo) - 1)))
+ yref[4*(i-l1)+1] = ytempmin
+ yref[4*(i-l1)+2] = ytempmin
+ yref[4*(i-l1)+3] = ytempmax
+ yref[4*(i-l1)+4] = ytempmax
+ }
+ }
+ case GT_TWO:
+ if (GT_NLINES(geo) == 1) {
+ yref[1] = 0.5
+ yref[2] = 1.5
+ } else {
+ do i = l1, l2 {
+ ytempmin = min (real (nysample), max (1.,
+ real ((nysample - 1) * (i - 0.5) + (GT_NLINES(geo) -
+ nysample)) / (GT_NLINES(geo) - 1)))
+ ytempmax = min (real (nysample), max (1.,
+ real ((nysample - 1) * (i + 0.5) + (GT_NLINES(geo) -
+ nysample)) / (GT_NLINES(geo) - 1)))
+ yref[2*(i-l1)+1] = ytempmin
+ yref[2*(i-l1)+2] = ytempmax
+ }
+ }
+ case GT_ONE:
+ if (GT_NLINES(geo) == 1)
+ yref[1] = 1.0
+ else {
+ do i = l1, l2
+ yref[i-l1+1] = min (real (nysample), max (1.,
+ real ((nysample - 1) * i + (GT_NLINES(geo) -
+ nysample)) / (GT_NLINES(geo) - 1)))
+ }
+ }
+end
+
+
+# GEO_XBUFFER -- Compute the x interpolant and coordinates.
+
+procedure geo_xbuffer (s1, s2, msi, xsample, ysample, c1, c2, l1, l2, buf)
+
+pointer s1, s2 #I pointers to the x surface
+pointer msi #I interpolant
+real xsample[ARB] #I sampled x reference coordinates
+real ysample[ARB] #I sampled y reference coordinates
+int c1, c2 #I columns of interest in sampled image
+int l1, l2 #I lines of interest in the sampled image
+pointer buf #I pointer to output buffer
+
+int i, ncols, nlines, llast1, llast2, nclast, nllast
+pointer sp, sf, y, z, buf1, buf2
+
+begin
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+
+ # Combine surfaces.
+ if (s2 == NULL)
+ call gscopy (s1, sf)
+ else
+ call gsadd (s1, s2, sf)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (y, ncols, TY_REAL)
+ call salloc (z, ncols, TY_REAL)
+
+ # If buffer undefined then allocate memory for the buffer. Reallocate
+ # the buffer if the number of lines or columns changes.
+ if (buf == NULL) {
+ call malloc (buf, ncols * nlines, TY_REAL)
+ llast1 = l1 - nlines
+ llast2 = l2 - nlines
+ } else if ((nlines != nllast) || (ncols != nclast)) {
+ call realloc (buf, ncols * nlines, TY_REAL)
+ llast1 = l1 - nlines
+ llast2 = l2 - nlines
+ }
+
+ # Compute the coordinates.
+ if (l1 < llast1) {
+ do i = l2, l1, -1 {
+ if (i > llast1)
+ buf1 = buf + (i - llast1) * ncols
+ else {
+ buf1 = z
+ call amovkr (ysample[i], Memr[y], ncols)
+ call gsvector (sf, xsample[c1], Memr[y], Memr[buf1], ncols)
+ }
+ buf2 = buf + (i - l1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ } else if (l2 > llast2) {
+ do i = l1, l2 {
+ if (i < llast2)
+ buf1 = buf + (i - llast1) * ncols
+ else {
+ buf1 = z
+ call amovkr (ysample[i], Memr[y], ncols)
+ call gsvector (sf, xsample[c1], Memr[y], Memr[buf1], ncols)
+ }
+ buf2 = buf + (i - l1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ }
+
+ llast1 = l1
+ llast2 = l2
+ nclast = ncols
+ nllast = nlines
+
+ # Fit the interpolant.
+ if (nlines == 1)
+ call asifit (msi, Memr[buf], ncols)
+ else
+ call msifit (msi, Memr[buf], ncols, nlines, ncols)
+
+ call gsfree (sf)
+ call sfree (sp)
+end
+
+
+# GEO_YBUFFER -- Compute the y interpolant and coordinates.
+
+procedure geo_ybuffer (s1, s2, msi, xsample, ysample, c1, c2, l1, l2, buf)
+
+pointer s1, s2 #I pointers to the y surface
+pointer msi #I interpolant
+real xsample[ARB] #I sampled x reference coordinates
+real ysample[ARB] #I sampled y reference coordinates
+int c1, c2 #I columns of interest in sampled image
+int l1, l2 #I lines of interest in the sampled image
+pointer buf #I pointer to output buffer
+
+int i, ncols, nlines, llast1, llast2, nclast, nllast
+pointer sp, sf, y, z, buf1, buf2
+
+begin
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+
+ # Combine surfaces.
+ if (s2 == NULL)
+ call gscopy (s1, sf)
+ else
+ call gsadd (s1, s2, sf)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (y, ncols, TY_REAL)
+ call salloc (z, ncols, TY_REAL)
+
+ # If buffer undefined then allocate memory for the buffer. Reallocate
+ # the buffer if the number of lines or columns changes.
+ if (buf == NULL) {
+ call malloc (buf, ncols * nlines, TY_REAL)
+ llast1 = l1 - nlines
+ llast2 = l2 - nlines
+ } else if ((nlines != nllast) || (ncols != nclast)) {
+ call realloc (buf, ncols * nlines, TY_REAL)
+ llast1 = l1 - nlines
+ llast2 = l2 - nlines
+ }
+
+ # Compute the coordinates.
+ if (l1 < llast1) {
+ do i = l2, l1, -1 {
+ if (i > llast1)
+ buf1 = buf + (i - llast1) * ncols
+ else {
+ buf1 = z
+ call amovkr (ysample[i], Memr[y], ncols)
+ call gsvector (sf, xsample[c1], Memr[y], Memr[buf1], ncols)
+ }
+ buf2 = buf + (i - l1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ } else if (l2 > llast2) {
+ do i = l1, l2 {
+ if (i < llast2)
+ buf1 = buf + (i - llast1) * ncols
+ else {
+ buf1 = z
+ call amovkr (ysample[i], Memr[y], ncols)
+ call gsvector (sf, xsample[c1], Memr[y], Memr[buf1], ncols)
+ }
+ buf2 = buf + (i - l1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ }
+
+ llast1 = l1
+ llast2 = l2
+ nclast = ncols
+ nllast = nlines
+
+ # Fit the interpolant.
+ if (nlines == 1)
+ call asifit (msi, Memr[buf], ncols)
+ else
+ call msifit (msi, Memr[buf], ncols, nlines, ncols)
+
+ call gsfree (sf)
+ call sfree (sp)
+end
+
+
+# GEO_JBUFFER -- Fit the jacobian surface.
+
+procedure geo_jbuffer (sx1, sy1, sx2, sy2, jmsi, xsample, ysample, c1, c2, l1,
+ l2, jbuf)
+
+pointer sx1, sy1 #I pointers to the linear surfaces
+pointer sx2, sy2 #I pointers to the distortion surfaces
+pointer jmsi #I interpolant
+real xsample[ARB] #I sampled x reference coordinates
+real ysample[ARB] #I sampled y reference coordinates
+int c1, c2 #I columns of interest in sampled image
+int l1, l2 #I lines of interest in the sampled image
+pointer jbuf #I pointer to output buffer
+
+int i, ncols, nlines, llast1, llast2, nclast, nllast
+pointer sp, sx, sy, y, z, buf1, buf2
+
+begin
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+
+ # Combine surfaces.
+ if (sx2 == NULL)
+ call gscopy (sx1, sx)
+ else
+ call gsadd (sx1, sx2, sx)
+ if (sy1 == NULL)
+ sy = NULL
+ else if (sy2 == NULL)
+ call gscopy (sy1, sy)
+ else
+ call gsadd (sy1, sy2, sy)
+
+ call smark (sp)
+ call salloc (y, ncols, TY_REAL)
+ call salloc (z, ncols, TY_REAL)
+
+ # If buffer undefined then allocate memory for the buffer. Reallocate
+ # the buffer if the number of lines or columns changes.
+ if (jbuf == NULL) {
+ call malloc (jbuf, ncols * nlines, TY_REAL)
+ llast1 = l1 - nlines
+ llast2 = l2 - nlines
+ } else if ((nlines != nllast) || (ncols != nclast)) {
+ call realloc (jbuf, ncols * nlines, TY_REAL)
+ llast1 = l1 - nlines
+ llast2 = l2 - nlines
+ }
+
+ # Compute surface.
+ if (l1 < llast1) {
+ do i = l2, l1, -1 {
+ if (i > llast1)
+ buf1 = jbuf + (i - llast1) * ncols
+ else {
+ buf1 = z
+ call amovkr (ysample[i], Memr[y], ncols)
+ call geo_jgsvector (sx, sy, xsample[c1], Memr[y],
+ Memr[buf1], ncols)
+ }
+ buf2 = jbuf + (i - l1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ } else if (l2 > llast2) {
+ do i = l1, l2 {
+ if (i < llast2)
+ buf1 = jbuf + (i - llast1) * ncols
+ else {
+ buf1 = z
+ call amovkr (ysample[i], Memr[y], ncols)
+ call geo_jgsvector (sx, sy, xsample[c1], Memr[y],
+ Memr[buf1], ncols)
+ }
+ buf2 = jbuf + (i - l1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ }
+
+ # Update buffer pointers.
+ llast1 = l1
+ llast2 = l2
+ nclast = ncols
+ nllast = nlines
+
+ # Fit the interpolant.
+ if (nlines == 1)
+ call asifit (jmsi, Memr[jbuf], ncols)
+ else
+ call msifit (jmsi, Memr[jbuf], ncols, nlines, ncols)
+
+ call gsfree (sx)
+ call gsfree (sy)
+ call sfree (sp)
+end
+
+
+# GEO_JGSVECTOR -- Procedure to compute the Jacobian of the transformation.
+
+procedure geo_jgsvector (sx, sy, x, y, out, ncols)
+
+pointer sx, sy #I surface descriptors
+real x[ARB] #I x values
+real y[ARB] #I y values
+real out[ARB] #O output values
+int ncols #I number of points
+
+pointer sp, der1, der2
+
+begin
+ call smark (sp)
+
+ if (sy == NULL) {
+ call gsder (sx, x, y, out, ncols, 1, 0)
+ } else {
+ call salloc (der1, ncols, TY_REAL)
+ call salloc (der2, ncols, TY_REAL)
+ call gsder (sx, x, y, Memr[der1], ncols, 1, 0)
+ call gsder (sy, x, y, Memr[der2], ncols, 0, 1)
+ call amulr (Memr[der1], Memr[der2], out, ncols)
+ call gsder (sx, x, y, Memr[der1], ncols, 0, 1)
+ call gsder (sy, x, y, Memr[der2], ncols, 1, 0)
+ call amulr (Memr[der1], Memr[der2], Memr[der1], ncols)
+ call asubr (out, Memr[der1], out, ncols)
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_MSIVECTOR -- Procedure to interpolate the surface coordinates
+
+procedure geo_msivector (in, out, geo, xmsi, ymsi, jmsi, msi, sx1, sy1, sx2,
+ sy2, xref, c1, c2, nxsample, yref, l1, l2, nysample, x0, y0)
+
+pointer in #I pointer to input image
+pointer out #I pointer to output image
+pointer geo #I pointer to geotran structure
+pointer xmsi, ymsi #I pointer to the interpolation cord surfaces
+pointer jmsi #I pointer to Jacobian surface
+pointer msi #I pointer to interpolation surface
+pointer sx1, sy1 #I pointers to linear surfaces
+pointer sx2, sy2 #I pointer to higher order surfaces
+real xref[ARB] #I x reference coordinates
+int c1, c2 #I column limits in output image
+int nxsample #I the x sample size
+real yref[ARB] #I y reference coordinates
+int l1, l2 #I line limits in output image
+int nysample #I the y sample size
+int x0, y0 #I zero points of interpolation coordinates
+
+int j, ncols, nlines, ncols4, nlines4
+int imc1, imc2, iml1, iml2, nicols, nilines
+pointer sp, txref, tyref, x, y, xin, yin, inbuf, outbuf
+real xmin, xmax, ymin, ymax, factor
+pointer imgs1r(), imgs2r(), imps1r(), imps2r()
+real geo_jfactor()
+
+begin
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+
+ # Find min max of interpolation coords.
+ if (IM_NDIM(in) == 1)
+ call geo_iminmax (xref, yref, c1, c2, l1, l2, x0, 0,
+ xmsi, ymsi, xmin, xmax, ymin, ymax)
+ else
+ call geo_iminmax (xref, yref, c1, c2, l1, l2, x0, y0,
+ xmsi, ymsi, xmin, xmax, ymin, ymax)
+
+ # Get the appropriate image section and fit the interpolant.
+ imc1 = int(xmin) - GT_NXYMARGIN(geo)
+ if (imc1 <= 0)
+ imc1 = imc1 - 1
+ imc2 = nint (xmax) + GT_NXYMARGIN(geo) + 1
+ nicols = imc2 - imc1 + 1
+ if (IM_NDIM(in) == 1) {
+ ncols4 = 2 * ncols
+ nlines4 = 2 * nlines
+ iml1 = 1
+ iml2 = 1
+ nilines = 1
+ inbuf = imgs1r (in, imc1, imc2)
+ if (inbuf == EOF)
+ call error (0, "Error reading image")
+ call asifit (msi, Memr[inbuf], nicols)
+ } else {
+ ncols4 = 4 * ncols
+ nlines4 = 4 * nlines
+ iml1 = int(ymin) - GT_NXYMARGIN(geo)
+ if (iml1 <= 0)
+ iml1 = iml1 - 1
+ iml2 = nint (ymax) + GT_NXYMARGIN(geo) + 1
+ nilines = iml2 - iml1 + 1
+ inbuf = imgs2r (in, imc1, imc2, iml1, iml2)
+ if (inbuf == EOF)
+ call error (0, "Error reading image")
+ call msifit (msi, Memr[inbuf], nicols, nilines, nicols)
+ }
+
+ # Allocate working space.
+ call smark (sp)
+ if (GT_INTERPOLANT(geo) == II_DRIZZLE || GT_INTERPOLANT(geo) ==
+ II_BIDRIZZLE) {
+ call salloc (txref, ncols4, TY_REAL)
+ call salloc (tyref, nlines4, TY_REAL)
+ call salloc (x, ncols4, TY_REAL)
+ call salloc (y, ncols4, TY_REAL)
+ call salloc (xin, ncols4, TY_REAL)
+ call salloc (yin, ncols4, TY_REAL)
+ if (IM_NDIM(in) == 1)
+ call geo_sample (geo, Memr[txref], c1, c2, nxsample,
+ Memr[tyref], l1, l2, nysample, GT_TWO)
+ else
+ call geo_sample (geo, Memr[txref], c1, c2, nxsample,
+ Memr[tyref], l1, l2, nysample, GT_FOUR)
+ call aaddkr (Memr[txref], real (-x0 + 1), Memr[x], ncols4)
+ } else {
+ call salloc (x, ncols, TY_REAL)
+ call salloc (y, ncols, TY_REAL)
+ call salloc (xin, ncols, TY_REAL)
+ call salloc (yin, ncols, TY_REAL)
+ call aaddkr (xref[c1], real (-x0 + 1), Memr[x], ncols)
+ }
+
+ # Compute the output buffer.
+ do j = l1, l2 {
+
+ # Write the output image.
+ if (IM_NDIM(in) == 1)
+ outbuf = imps1r (out, c1, c2)
+ else
+ outbuf = imps2r (out, c1, c2, j, j)
+ if (outbuf == EOF)
+ call error (0, "Error writing output image")
+
+ # Compute the interpolation coordinates.
+ if (GT_INTERPOLANT(geo) == II_DRIZZLE || GT_INTERPOLANT(geo) ==
+ II_BIDRIZZLE) {
+ if (IM_NDIM(in) == 1) {
+ call asivector (xmsi, Memr[x], Memr[xin], ncols4)
+ call amovkr (1.0, Memr[yin], ncols4)
+ } else {
+ #call amovkr (yref[j] + real (-y0 + 1), Memr[y], ncols)
+ call geo_repeat (Memr[tyref+4*(j-l1)], 4, Memr[y], ncols)
+ call aaddkr (Memr[y], real(-y0 + 1), Memr[y], ncols4)
+ call msivector (xmsi, Memr[x], Memr[y], Memr[xin], ncols4)
+ call msivector (ymsi, Memr[x], Memr[y], Memr[yin], ncols4)
+ }
+ if (imc1 != 1)
+ call aaddkr (Memr[xin], real (-imc1 + 1), Memr[xin], ncols4)
+ if (iml1 != 1)
+ call aaddkr (Memr[yin], real (-iml1 + 1), Memr[yin], ncols4)
+ } else {
+ if (IM_NDIM(in) == 1) {
+ call asivector (xmsi, Memr[x], Memr[xin], ncols)
+ call amovkr (1.0, Memr[yin], ncols)
+ } else {
+ call amovkr (yref[j] + real (-y0 + 1), Memr[y], ncols)
+ call msivector (xmsi, Memr[x], Memr[y], Memr[xin], ncols)
+ call msivector (ymsi, Memr[x], Memr[y], Memr[yin], ncols)
+ }
+ if (imc1 != 1)
+ call aaddkr (Memr[xin], real (-imc1 + 1), Memr[xin], ncols)
+ if (iml1 != 1)
+ call aaddkr (Memr[yin], real (-iml1 + 1), Memr[yin], ncols)
+ }
+
+ # Interpolate in the input image.
+ if (IM_NDIM(in) == 1)
+ call asivector (msi, Memr[xin], Memr[outbuf], ncols)
+ else
+ call msivector (msi, Memr[xin], Memr[yin], Memr[outbuf], ncols)
+
+ # Preserve flux in image.
+ if (GT_FLUXCONSERVE(geo) == YES) {
+ factor = GT_XSCALE(geo) * GT_YSCALE(geo)
+ if (GT_GEOMODE(geo) == GT_LINEAR || (sx2 == NULL && sy2 ==
+ NULL)) {
+ if (IM_NDIM(in) == 1)
+ call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+ NULL), Memr[outbuf], ncols)
+ else
+ call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+ sy1), Memr[outbuf], ncols)
+ } else {
+ if (IM_NDIM(in) == 1)
+ call geo_msiflux (jmsi, xref, yref, Memr[outbuf],
+ c1, c2, 0, x0, y0)
+ else
+ call geo_msiflux (jmsi, xref, yref, Memr[outbuf],
+ c1, c2, j, x0, y0)
+ call amulkr (Memr[outbuf], factor, Memr[outbuf], ncols)
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_GSVECTOR -- Evaluate the output image pixels using fitted coordinate
+# values and image interpolation.
+
+procedure geo_gsvector (input, output, geo, msi, xref, c1, c2, yref, l1, l2,
+ sx1, sy1, sx2, sy2)
+
+pointer input #I pointer to input image
+pointer output #I pointer to output image
+pointer geo #I pointer to geotran structure
+pointer msi #I pointer to interpolant
+real xref[ARB] #I x reference array
+int c1, c2 #I columns of interest in output image
+real yref[ARB] #I y reference array
+int l1, l2 #I lines of interest in the output image
+pointer sx1, sy1 #I linear surface descriptors
+pointer sx2, sy2 #I distortion surface descriptors
+
+int j, ncols, nlines, ncols4, nlines4, nicols, nilines
+int imc1, imc2, iml1, iml2
+pointer sp, txref, tyref, y, xin, yin, temp, inbuf, outbuf
+real xmin, xmax, ymin, ymax, factor
+pointer imgs1r(), imgs2r(), imps1r(), imps2r()
+real gsgetr(), geo_jfactor()
+
+begin
+ # Compute the number of columns.
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+
+ # Compute the maximum and minimum coordinates.
+ call geo_minmax (xref, yref, c1, c2, l1, l2, sx1, sy1, sx2, sy2,
+ xmin, xmax, ymin, ymax)
+
+ # Get the appropriate image section and fill the buffer.
+ imc1 = int(xmin) - GT_NXYMARGIN(geo)
+ if (imc1 <= 0)
+ imc1 = imc1 - 1
+ imc2 = nint (xmax) + GT_NXYMARGIN(geo) + 1
+ nicols = imc2 - imc1 + 1
+ if (IM_NDIM(input) == 1) {
+ iml1 = 1
+ iml2 = 1
+ nilines = 1
+ ncols4 = 2 * ncols
+ nlines4 = 2 * nlines
+ inbuf = imgs1r (input, imc1, imc2)
+ if (inbuf == EOF)
+ call error (0, "Error reading image")
+ call asifit (msi, Memr[inbuf], nicols)
+ } else {
+ iml1 = int(ymin) - GT_NXYMARGIN(geo)
+ if (iml1 <= 0)
+ iml1 = iml1 - 1
+ iml2 = nint (ymax) + GT_NXYMARGIN(geo) + 1
+ nilines = iml2 - iml1 + 1
+ ncols4 = 4 * ncols
+ nlines4 = 4 * nlines
+ inbuf = imgs2r (input, imc1, imc2, iml1, iml2)
+ if (inbuf == EOF)
+ call error (0, "Error reading image")
+ call msifit (msi, Memr[inbuf], nicols, nilines, nicols)
+ }
+
+ # Allocate working space.
+ call smark (sp)
+ if (GT_INTERPOLANT(geo) == II_DRIZZLE || GT_INTERPOLANT(geo) ==
+ II_BIDRIZZLE) {
+ call salloc (txref, ncols4, TY_REAL)
+ call salloc (tyref, nlines4, TY_REAL)
+ call salloc (y, ncols4, TY_REAL)
+ call salloc (xin, ncols4, TY_REAL)
+ call salloc (yin, ncols4, TY_REAL)
+ call salloc (temp, ncols4, TY_REAL)
+ if (IM_NDIM(input) == 1)
+ call geo_ref (geo, Memr[txref], c1, c2, GT_NCOLS(geo),
+ Memr[tyref], l1, l2, GT_NLINES(geo), gsgetr (sx1, GSXMIN),
+ gsgetr (sx1, GSXMAX), gsgetr (sx1, GSYMIN), gsgetr (sx1,
+ GSYMAX), GT_TWO)
+ else
+ call geo_ref (geo, Memr[txref], c1, c2, GT_NCOLS(geo),
+ Memr[tyref], l1, l2, GT_NLINES(geo), gsgetr (sx1, GSXMIN),
+ gsgetr (sx1, GSXMAX), gsgetr (sx1, GSYMIN), gsgetr (sx1,
+ GSYMAX), GT_FOUR)
+ } else {
+ call salloc (y, ncols, TY_REAL)
+ call salloc (xin, ncols, TY_REAL)
+ call salloc (yin, ncols, TY_REAL)
+ call salloc (temp, ncols, TY_REAL)
+ }
+
+ # Compute the pixels.
+ do j = l1, l2 {
+
+ # Get output image buffer.
+ if (IM_NDIM(input) == 1)
+ outbuf = imps1r (output, c1, c2)
+ else
+ outbuf = imps2r (output, c1, c2, j, j)
+ if (output == EOF)
+ call error (0, "Error writing output image")
+
+ # Compute the interpolation coordinates.
+ if (GT_INTERPOLANT(geo) == II_DRIZZLE || GT_INTERPOLANT(geo) ==
+ II_BIDRIZZLE) {
+
+ # Set the y coordinate.
+ if (IM_NDIM(input) == 1)
+ call geo_repeat (Memr[tyref+2*(j-l1)], 2, Memr[y], ncols)
+ else
+ call geo_repeat (Memr[tyref+4*(j-l1)], 4, Memr[y], ncols)
+
+ # Fit x coords.
+ call gsvector (sx1, Memr[txref], Memr[y], Memr[xin], ncols4)
+ if (sx2 != NULL) {
+ call gsvector (sx2, Memr[txref], Memr[y], Memr[temp],
+ ncols4)
+ call aaddr (Memr[xin], Memr[temp], Memr[xin], ncols4)
+ }
+ if (imc1 != 1)
+ call aaddkr (Memr[xin], real (-imc1 + 1), Memr[xin], ncols4)
+
+ # Fit y coords.
+ call gsvector (sy1, Memr[txref], Memr[y], Memr[yin], ncols4)
+ if (sy2 != NULL) {
+ call gsvector (sy2, Memr[txref], Memr[y], Memr[temp],
+ ncols4)
+ call aaddr (Memr[yin], Memr[temp], Memr[yin], ncols4)
+ }
+ if (iml1 != 1)
+ call aaddkr (Memr[yin], real (-iml1 + 1), Memr[yin], ncols4)
+
+ } else {
+
+ # Set the y coordinate.
+ call amovkr (yref[j], Memr[y], ncols)
+
+ # Fit x coords.
+ call gsvector (sx1, xref[c1], Memr[y], Memr[xin], ncols)
+ if (sx2 != NULL) {
+ call gsvector (sx2, xref[c1], Memr[y], Memr[temp], ncols)
+ call aaddr (Memr[xin], Memr[temp], Memr[xin], ncols)
+ }
+ if (imc1 != 1)
+ call aaddkr (Memr[xin], real (-imc1 + 1), Memr[xin], ncols)
+
+ # Fit y coords.
+ call gsvector (sy1, xref[c1], Memr[y], Memr[yin], ncols)
+ if (sy2 != NULL) {
+ call gsvector (sy2, xref[c1], Memr[y], Memr[temp], ncols)
+ call aaddr (Memr[yin], Memr[temp], Memr[yin], ncols)
+ }
+ if (iml1 != 1)
+ call aaddkr (Memr[yin], real (-iml1 + 1), Memr[yin], ncols)
+ }
+
+ # Interpolate in input image.
+ if (IM_NDIM(input) == 1)
+ call asivector (msi, Memr[xin], Memr[outbuf], ncols)
+ else
+ call msivector (msi, Memr[xin], Memr[yin], Memr[outbuf], ncols)
+
+ # Preserve flux in image.
+ if (GT_FLUXCONSERVE(geo) == YES) {
+ factor = GT_XSCALE(geo) * GT_YSCALE(geo)
+ if (GT_GEOMODE(geo) == GT_LINEAR || (sx2 == NULL && sy2 ==
+ NULL)) {
+ if (IM_NDIM(input) == 1)
+ call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+ NULL), Memr[outbuf], ncols)
+ else
+ call amulkr (Memr[outbuf], factor * geo_jfactor (sx1,
+ sy1), Memr[outbuf], ncols)
+ } else {
+ if (IM_NDIM(input) == 1)
+ call geo_gsflux (xref, yref, Memr[outbuf], c1, c2, j,
+ sx1, NULL, sx2, NULL)
+ else
+ call geo_gsflux (xref, yref, Memr[outbuf], c1, c2, j,
+ sx1, sy1, sx2, sy2)
+ call amulkr (Memr[outbuf], factor, Memr[outbuf], ncols)
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_IMINMAX -- Find minimum and maximum interpolation coordinates.
+
+procedure geo_iminmax (xref, yref, c1, c2, l1, l2, x0, y0, xmsi, ymsi, xmin,
+ xmax, ymin, ymax)
+
+real xref[ARB] #I x reference coords
+real yref[ARB] #I y reference coords
+int c1, c2 #I columns limits
+int l1, l2 #I line limits
+int x0, y0 #I interpolation coord zero points
+pointer xmsi, ymsi #I coord surfaces
+real xmin, xmax #O output xmin and xmax
+real ymin, ymax #O output ymin and ymax
+
+int j, ncols
+pointer sp, x, y, xin, yin
+real mintemp, maxtemp, x1, x2, y1, y2
+real asieval(), msieval()
+
+begin
+ call smark (sp)
+ ncols = c2 - c1 + 1
+ call salloc (x, ncols, TY_REAL)
+ call salloc (y, ncols, TY_REAL)
+ call salloc (xin, ncols, TY_REAL)
+ call salloc (yin, ncols, TY_REAL)
+
+ xmin = MAX_REAL
+ xmax = -MAX_REAL
+ ymin = MAX_REAL
+ ymax = -MAX_REAL
+
+ # find the minimum and maximum
+ do j = l1, l2 {
+
+ if (j == l1 || j == l2) {
+
+ call aaddkr (xref[c1], real (-x0 + 1), Memr[x], ncols)
+ if (y0 <= 0) {
+ call asivector (xmsi, Memr[x], Memr[xin], ncols)
+ ymin = 1.0
+ ymax = 1.0
+ } else {
+ call amovkr (yref[j] + real (-y0 + 1), Memr[y], ncols)
+ call msivector (xmsi, Memr[x], Memr[y], Memr[xin], ncols)
+ call msivector (ymsi, Memr[x], Memr[y], Memr[yin], ncols)
+ call alimr (Memr[yin], ncols, mintemp, maxtemp)
+ ymin = min (ymin, mintemp)
+ ymax = max (ymax, maxtemp)
+ }
+ call alimr (Memr[xin], ncols, mintemp, maxtemp)
+ xmin = min (xmin, mintemp)
+ xmax = max (xmax, maxtemp)
+ } else {
+ if (y0 <= 0) {
+ x1 = asieval (xmsi, xref[c1] + real (-x0 + 1))
+ x2 = asieval (xmsi, xref[c1+ncols-1] + real (-x0 + 1))
+ ymin = 1.0
+ ymax = 1.0
+ } else {
+ x1 = msieval (xmsi, xref[c1] + real (-x0 + 1),
+ yref[j] + real (-y0 + 1))
+ x2 = msieval (xmsi, xref[c1+ncols-1] + real (-x0 + 1),
+ yref[j] + real (-y0 + 1))
+ y1 = msieval (ymsi, xref[c1] + real (-x0 + 1),
+ yref[j] + real (-y0 + 1))
+ y2 = msieval (ymsi, xref[c1+ncols-1] + real (-x0 + 1),
+ yref[j] + real (-y0 + 1))
+ ymin = min (ymin, y1, y2)
+ ymax = max (ymax, y1, y2)
+ }
+ xmin = min (xmin, x1, x2)
+ xmax = max (xmax, x1, x2)
+
+ }
+ }
+
+ call sfree (sp)
+
+end
+
+
+# GEO_MINMAX -- Compute the minimum and maximum fitted coordinates.
+
+procedure geo_minmax (xref, yref, c1, c2, l1, l2, sx1, sy1, sx2, sy2,
+ xmin, xmax, ymin, ymax)
+
+real xref[ARB] #I x reference coords
+real yref[ARB] #I y reference coords
+int c1, c2 #I columns limits
+int l1, l2 #I line limits
+pointer sx1, sy1 #I linear surface descriptors
+pointer sx2, sy2 #I distortion surface descriptors
+real xmin, xmax #O output xmin and xmax
+real ymin, ymax #O output ymin and ymax
+
+int j, ncols
+pointer sp, y, xin, yin, temp
+real x1, x2, y1, y2, mintemp, maxtemp
+real gseval()
+
+begin
+ call smark (sp)
+ ncols = c2 - c1 + 1
+ call salloc (y, ncols, TY_REAL)
+ call salloc (xin, ncols, TY_REAL)
+ call salloc (yin, ncols, TY_REAL)
+ call salloc (temp, ncols, TY_REAL)
+
+ xmin = MAX_REAL
+ xmax = -MAX_REAL
+ ymin = MAX_REAL
+ ymax = -MAX_REAL
+
+ # Find the maximum and minimum coordinates.
+ do j = l1, l2 {
+
+ if (j == l1 || j == l2) {
+
+ call amovkr (yref[j], Memr[y], ncols)
+ call gsvector (sx1, xref[c1], Memr[y], Memr[xin], ncols)
+ if (sx2 != NULL) {
+ call gsvector (sx2, xref[c1], Memr[y], Memr[temp], ncols)
+ call aaddr (Memr[xin], Memr[temp], Memr[xin], ncols)
+ }
+ call gsvector (sy1, xref[c1], Memr[y], Memr[yin], ncols)
+ if (sy2 != NULL) {
+ call gsvector (sy2, xref[c1], Memr[y], Memr[temp], ncols)
+ call aaddr (Memr[yin], Memr[temp], Memr[yin], ncols)
+ }
+
+ call alimr (Memr[xin], ncols, mintemp, maxtemp)
+ xmin = min (xmin, mintemp)
+ xmax = max (xmax, maxtemp)
+ call alimr (Memr[yin], ncols, mintemp, maxtemp)
+ ymin = min (ymin, mintemp)
+ ymax = max (ymax, maxtemp)
+
+ } else {
+
+ x1 = gseval (sx1, xref[c1], yref[j])
+ x2 = gseval (sx1, xref[c1+ncols-1], yref[j])
+ if (sx2 != NULL) {
+ x1 = x1 + gseval (sx2, xref[c1], yref[j])
+ x2 = x2 + gseval (sx2, xref[c1+ncols-1], yref[j])
+ }
+ xmin = min (xmin, x1, x2)
+ xmax = max (xmax, x1, x2)
+
+ y1 = gseval (sy1, xref[c1], yref[j])
+ y2 = gseval (sy1, xref[c1+ncols-1], yref[j])
+ if (sy2 != NULL) {
+ y1 = y1 + gseval (sy2, xref[c1], yref[j])
+ y2 = y2 + gseval (sy2, xref[c1+ncols-1], yref[j])
+ }
+ ymin = min (ymin, y1, y2)
+ ymax = max (ymax, y1, y2)
+
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_MARGSET -- Set up interpolation margin
+
+procedure geo_margset (sx1, sy1, sx2, sy2, xmin, xmax, ncols, ymin, ymax,
+ nlines, interpolant, nsinc, nxymargin)
+
+pointer sx1, sy1 #I linear surface descriptors
+pointer sx2, sy2 #I distortion surface descriptors
+real xmin, xmax #I the reference coordinate x limits
+int ncols #I the number of output image columns
+real ymin, ymax #I the reference coordinate y limits
+int nlines #I the number of output image lines
+int interpolant #I the interpolant type
+int nsinc #I the sinc width
+int nxymargin #O the interpolation margin
+
+int dist1, dist2, dist3, dist4, dist5, dist6
+pointer newsx, newsy
+real x1, y1, x2, y2
+real gseval()
+
+begin
+ if (interpolant == II_SPLINE3 || interpolant == II_BISPLINE3) {
+ nxymargin = NMARGIN_SPLINE3
+ } else if (interpolant == II_LSINC || interpolant == II_BILSINC) {
+ nxymargin = nsinc
+ } else if (interpolant == II_SINC || interpolant == II_BISINC) {
+ nxymargin = nsinc
+ } else if (interpolant == II_DRIZZLE || interpolant == II_BIDRIZZLE) {
+ if (sx2 == NULL)
+ call gscopy (sx1, newsx)
+ else
+ call gsadd (sx1, sx2, newsx)
+ if (sy2 == NULL)
+ call gscopy (sy1, newsy)
+ else
+ call gsadd (sy1, sy2, newsy)
+ x1 = gseval (newsx, xmin, ymin)
+ y1 = gseval (newsy, xmin, ymin)
+ x2 = gseval (newsx, xmax, ymin)
+ y2 = gseval (newsy, xmax, ymin)
+ dist1 = sqrt ((x1 - x2) ** 2 + (y1 - y2) ** 2) / ncols
+ x1 = gseval (newsx, xmax, ymax)
+ y1 = gseval (newsy, xmax, ymax)
+ dist2 = sqrt ((x1 - x2) ** 2 + (y1 - y2) ** 2) / nlines
+ x2 = gseval (newsx, xmin, ymax)
+ y2 = gseval (newsy, xmin, ymax)
+ dist3 = sqrt ((x1 - x2) ** 2 + (y1 - y2) ** 2) / ncols
+ x1 = gseval (newsx, xmin, ymin)
+ y1 = gseval (newsy, xmin, ymin)
+ dist4 = sqrt ((x1 - x2) ** 2 + (y1 - y2) ** 2) / nlines
+ x1 = gseval (newsx, xmin, (ymin + ymax) / 2.0)
+ y1 = gseval (newsy, xmin, (ymin + ymax) / 2.0)
+ x2 = gseval (newsx, xmax, (ymin + ymax) / 2.0)
+ y2 = gseval (newsy, xmax, (ymin + ymax) / 2.0)
+ dist5 = sqrt ((x1 - x2) ** 2 + (y1 - y2) ** 2) / ncols
+ x1 = gseval (newsx, (xmin + xmax) / 2.0, ymin)
+ y1 = gseval (newsy, (xmin + xmax) / 2.0, ymin)
+ x2 = gseval (newsx, (xmin + xmax) / 2.0, ymax)
+ y2 = gseval (newsy, (xmin + xmax) / 2.0, ymax)
+ dist6 = sqrt ((x1 - x2) ** 2 + (y1 - y2) ** 2) / nlines
+ nxymargin = max (NMARGIN, dist1, dist2, dist3, dist4,
+ dist5, dist6)
+ call gsfree (newsx)
+ call gsfree (newsy)
+ } else {
+ nxymargin = NMARGIN
+ }
+end
+
+
+# GEO_IMSET -- Set up input image boundary conditions.
+
+procedure geo_imset (im, geo, sx1, sy1, sx2, sy2, xref, nx, yref, ny)
+
+pointer im #I pointer to image
+pointer geo #I pointer to geotran structure
+pointer sx1, sy1 #I linear surface descriptors
+pointer sx2, sy2 #I distortion surface descriptors
+real xref[ARB] #I x reference coordinates
+int nx #I number of x reference coordinates
+real yref[ARB] #I y reference coordinates
+int ny #I number of y reference coordinates
+
+int bndry, npts
+pointer sp, x1, x2, y1, y2, xtemp, ytemp
+real xn1, xn2, xn3, xn4, yn1, yn2, yn3, yn4, xmin, xmax, ymin, ymax
+real gseval()
+
+begin
+ npts = max (nx, ny)
+
+ xn1 = gseval (sx1, GT_XMIN(geo), GT_YMIN(geo))
+ xn2 = gseval (sx1, GT_XMAX(geo), GT_YMIN(geo))
+ xn3 = gseval (sx1, GT_XMAX(geo), GT_YMAX(geo))
+ xn4 = gseval (sx1, GT_XMIN(geo), GT_YMAX(geo))
+
+ yn1 = gseval (sy1, GT_XMIN(geo), GT_YMIN(geo))
+ yn2 = gseval (sy1, GT_XMAX(geo), GT_YMIN(geo))
+ yn3 = gseval (sy1, GT_XMAX(geo), GT_YMAX(geo))
+ yn4 = gseval (sy1, GT_XMIN(geo), GT_YMAX(geo))
+
+ xmin = min (xn1, xn2, xn3, xn4)
+ ymin = min (yn1, yn2, yn3, yn4)
+ xmax = max (xn1, xn2, xn3, xn4)
+ ymax = max (yn1, yn2, yn3, yn4)
+
+ if (sx2 != NULL) {
+ call smark (sp)
+ call salloc (x1, npts, TY_REAL)
+ call salloc (x2, npts, TY_REAL)
+ call salloc (xtemp, npts, TY_REAL)
+ call salloc (ytemp, npts, TY_REAL)
+
+ call amovkr (GT_YMIN(geo), Memr[ytemp], nx)
+ call gsvector (sx1, xref, Memr[ytemp], Memr[x1], nx)
+ call gsvector (sx2, xref, Memr[ytemp], Memr[x2], nx)
+ call aaddr (Memr[x1], Memr[x2], Memr[x1], nx)
+ call alimr (Memr[x1], nx, xn1, yn1)
+
+ call amovkr (GT_XMAX(geo), Memr[xtemp], ny)
+ call gsvector (sx1, Memr[xtemp], yref, Memr[x1], ny)
+ call gsvector (sx2, Memr[xtemp], yref, Memr[x2], ny)
+ call aaddr (Memr[x1], Memr[x2], Memr[x1], ny)
+ call alimr (Memr[x1], ny, xn2, yn2)
+
+ call amovkr (GT_YMAX(geo), Memr[ytemp], nx)
+ call gsvector (sx1, xref, Memr[ytemp], Memr[x1], nx)
+ call gsvector (sx2, xref, Memr[ytemp], Memr[x2], nx)
+ call aaddr (Memr[x1], Memr[x2], Memr[x1], nx)
+ call alimr (Memr[x1], nx, xn3, yn3)
+
+ call amovkr (GT_XMIN(geo), Memr[xtemp], ny)
+ call gsvector (sx1, Memr[xtemp], yref, Memr[x1], ny)
+ call gsvector (sx2, Memr[xtemp], yref, Memr[x2], ny)
+ call aaddr (Memr[x1], Memr[x2], Memr[x1], ny)
+ call alimr (Memr[x1], ny, xn4, yn4)
+
+ xmin = min (xn1, xn2, xn3, xn4)
+ xmax = max (yn1, yn2, yn3, yn4)
+
+ call sfree (sp)
+ }
+
+ if (sy2 != NULL) {
+ call smark (sp)
+ call salloc (y1, npts, TY_REAL)
+ call salloc (y2, npts, TY_REAL)
+ call salloc (xtemp, npts, TY_REAL)
+ call salloc (ytemp, npts, TY_REAL)
+
+ call amovkr (GT_YMIN(geo), Memr[ytemp], nx)
+ call gsvector (sy1, xref, Memr[ytemp], Memr[y1], nx)
+ call gsvector (sy2, xref, Memr[ytemp], Memr[y2], nx)
+ call aaddr (Memr[y1], Memr[y2], Memr[y1], nx)
+ call alimr (Memr[y1], nx, xn1, yn1)
+
+ call amovkr (GT_XMAX(geo), Memr[xtemp], ny)
+ call gsvector (sy1, Memr[xtemp], yref, Memr[y1], ny)
+ call gsvector (sy2, Memr[xtemp], yref, Memr[y2], ny)
+ call aaddr (Memr[y1], Memr[y2], Memr[y1], ny)
+ call alimr (Memr[y1], ny, xn2, yn2)
+
+ call amovkr (GT_YMAX(geo), Memr[ytemp], nx)
+ call gsvector (sy1, xref, Memr[ytemp], Memr[y1], nx)
+ call gsvector (sy2, xref, Memr[ytemp], Memr[y2], nx)
+ call aaddr (Memr[y1], Memr[y2], Memr[y1], nx)
+ call alimr (Memr[y1], nx, xn3, yn3)
+
+ call amovkr (GT_XMIN(geo), Memr[xtemp], ny)
+ call gsvector (sy1, Memr[xtemp], yref, Memr[y1], ny)
+ call gsvector (sy2, Memr[xtemp], yref, Memr[y2], ny)
+ call aaddr (Memr[y1], Memr[y2], Memr[y1], ny)
+ call alimr (Memr[y1], ny, xn4, yn4)
+
+ ymin = min (xn1, xn2, xn3, xn4)
+ ymax = max (yn1, yn2, yn3, yn4)
+
+ call sfree (sp)
+ }
+
+ # Compute the out-of-bounds limit.
+ if (IM_NDIM(im) == 1) {
+ if (xmin < 1.0 || xmax > real (IM_LEN(im,1)))
+ bndry = max (1.0 - xmin, xmax - IM_LEN(im,1)) + 1
+ else
+ bndry = 1
+ } else {
+ if (xmin < 1.0 || ymin < 1.0 || xmax > real (IM_LEN(im,1)) ||
+ ymax > real (IM_LEN(im,2)))
+ bndry = max (1.0 - xmin, 1.0 - ymin, xmax - IM_LEN(im,1),
+ ymax - IM_LEN(im,2)) + 1
+ else
+ bndry = 1
+ }
+
+ call imseti (im, IM_NBNDRYPIX, bndry + GT_NXYMARGIN(geo) + 1)
+ call imseti (im, IM_TYBNDRY, GT_BOUNDARY(geo))
+ call imsetr (im, IM_BNDRYPIXVAL, GT_CONSTANT(geo))
+end
+
+
+# GEO_GSFLUX -- Preserve the image flux after a transformation.
+
+procedure geo_gsflux (xref, yref, buf, c1, c2, line, sx1, sy1, sx2, sy2)
+
+real xref[ARB] #I x reference coordinates
+real yref[ARB] #I y reference coordinates
+real buf[ARB] #O output image buffer
+int c1, c2 #I column limits in the output image
+int line #I line in the output image
+pointer sx1, sy1 #I linear surface descriptors
+pointer sx2, sy2 #I distortion surface descriptors
+
+int ncols
+pointer sp, y, der1, der2, jacob, sx, sy
+
+begin
+ ncols = c2 - c1 + 1
+
+ # Get the reference coordinates.
+ call smark (sp)
+ call salloc (y, ncols, TY_REAL)
+ call salloc (jacob, ncols, TY_REAL)
+
+ # Add the two surfaces together for efficiency.
+ if (sx2 != NULL)
+ call gsadd (sx1, sx2, sx)
+ else
+ call gscopy (sx1, sx)
+ if (sy1 == NULL)
+ sy = NULL
+ else if (sy2 != NULL)
+ call gsadd (sy1, sy2, sy)
+ else
+ call gscopy (sy1, sy)
+
+ # Multiply the output buffer by the Jacobian.
+ call amovkr (yref[line], Memr[y], ncols)
+ if (sy == NULL)
+ call gsder (sx, xref[c1], Memr[y], Memr[jacob], ncols, 1, 0)
+ else {
+ call salloc (der1, ncols, TY_REAL)
+ call salloc (der2, ncols, TY_REAL)
+ call gsder (sx, xref[c1], Memr[y], Memr[der1], ncols, 1, 0)
+ call gsder (sy, xref[c1], Memr[y], Memr[der2], ncols, 0, 1)
+ call amulr (Memr[der1], Memr[der2], Memr[jacob], ncols)
+ call gsder (sx, xref[c1], Memr[y], Memr[der1], ncols, 0, 1)
+ call gsder (sy, xref[c1], Memr[y], Memr[der2], ncols, 1, 0)
+ call amulr (Memr[der1], Memr[der2], Memr[der1], ncols)
+ call asubr (Memr[jacob], Memr[der1], Memr[jacob], ncols)
+ }
+ call aabsr (Memr[jacob], Memr[jacob], ncols)
+ call amulr (buf, Memr[jacob], buf, ncols)
+
+ # Clean up.
+ call gsfree (sx)
+ if (sy != NULL)
+ call gsfree (sy)
+ call sfree (sp)
+end
+
+
+# GEO_MSIFLUX -- Procedure to interpolate the surface coordinates
+
+procedure geo_msiflux (jmsi, xinterp, yinterp, outdata, c1, c2, line, x0, y0)
+
+pointer jmsi #I pointer to the jacobian interpolant
+real xinterp[ARB] #I x reference coordinates
+real yinterp[ARB] #I y reference coordinates
+real outdata[ARB] #O output data
+int c1, c2 #I column limits in output image
+int line #I line to be flux corrected
+int x0, y0 #I zero points of interpolation coordinates
+
+int ncols
+pointer sp, x, y, jacob
+
+begin
+ # Allocate tempoaray space.
+ call smark (sp)
+ ncols = c2 - c1 + 1
+ call salloc (x, ncols, TY_REAL)
+ call salloc (jacob, ncols, TY_REAL)
+
+ # Calculate the x points.
+ if (x0 == 1)
+ call amovr (xinterp[c1], Memr[x], ncols)
+ else
+ call aaddkr (xinterp[c1], real (-x0 + 1), Memr[x], ncols)
+
+ # Multiply the data by the Jacobian.
+ if (line == 0) {
+ call asivector (jmsi, Memr[x], Memr[jacob], ncols)
+ } else {
+ call salloc (y, ncols, TY_REAL)
+ call amovkr ((yinterp[line] + real (-y0 + 1)), Memr[y], ncols)
+ call msivector (jmsi, Memr[x], Memr[y], Memr[jacob], ncols)
+ }
+ call aabsr (Memr[jacob], Memr[jacob], ncols)
+ call amulr (outdata, Memr[jacob], outdata, ncols)
+
+ call sfree (sp)
+end
+
+
+# GEO_JFACTOR -- Compute the Jacobian of a linear transformation.
+
+real procedure geo_jfactor (sx1, sy1)
+
+pointer sx1 #I pointer to x surface
+pointer sy1 #I pointer to y surface
+
+real xval, yval, xx, xy, yx, yy
+real gsgetr()
+
+begin
+ xval = (gsgetr (sx1, GSXMIN) + gsgetr (sx1, GSXMAX)) / 2.0
+ if (sy1 == NULL)
+ yval = 1.0
+ else
+ yval = (gsgetr (sy1, GSYMIN) + gsgetr (sy1, GSYMIN)) / 2.0
+
+ call gsder (sx1, xval, yval, xx, 1, 1, 0)
+ if (sy1 == NULL) {
+ xy = 0.0
+ yy = 1.0
+ yx = 0.0
+ } else {
+ call gsder (sx1, xval, yval, xy, 1, 0, 1)
+ call gsder (sy1, xval, yval, yx, 1, 1, 0)
+ call gsder (sy1, xval, yval, yy, 1, 0, 1)
+ }
+
+ return (abs (xx * yy - xy * yx))
+end
+
+
+# GEO_REPEAT -- Copy a small repeated pattern into the output buffer.
+
+procedure geo_repeat (pat, npat, output, ntimes)
+
+real pat[ARB] #I the input pattern to be repeated
+int npat #I the size of the pattern
+real output[ARB] #O the output array
+int ntimes #I the number of times the pattern is to be repeated
+
+int j, i, offset
+
+begin
+ do j = 1, ntimes {
+ offset = npat * j - npat
+ do i = 1, npat
+ output[offset+i] = pat[i]
+ }
+end
diff --git a/pkg/images/immatch/src/geometry/geoxytran.gx b/pkg/images/immatch/src/geometry/geoxytran.gx
new file mode 100644
index 00000000..22d577f1
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/geoxytran.gx
@@ -0,0 +1,327 @@
+include <ctype.h>
+include <mach.h>
+include <math.h>
+include <math/gsurfit.h>
+
+define GEO_LINEAR 1 # Linear transformation only
+define GEO_DISTORTION 2 # Distortion correction only
+define GEO_GEOMETRIC 3 # Full transformation
+
+$for (rd)
+
+# GEO_LINIT -- Initialize the linear part of the transformation.
+
+$if (datatype == r)
+procedure geo_linitr (sx1, sy1, sx2, sy2)
+$else
+procedure geo_linitd (sx1, sy1, sx2, sy2)
+$endif
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+pointer sx2, sy2 #I/O pointer to the distortion x and y surfaces
+
+PIXEL xmag, ymag, xrot, yrot, xref, yref, xout, yout, xshift, yshift
+$if (datatype == r)
+real clgetr(), gseval()
+$else
+double clgetd(), dgseval()
+$endif
+
+begin
+ # Initialize the surfaces.
+$if (datatype == r)
+ call gsinit (sx1, GS_POLYNOMIAL, 2, 2, GS_XNONE, -MAX_REAL, MAX_REAL,
+ -MAX_REAL, MAX_REAL)
+ call gsinit (sy1, GS_POLYNOMIAL, 2, 2, GS_XNONE, -MAX_REAL, MAX_REAL,
+ -MAX_REAL, MAX_REAL)
+$else
+ call dgsinit (sx1, GS_POLYNOMIAL, 2, 2, GS_XNONE, double (-MAX_REAL),
+ double (MAX_REAL), double (-MAX_REAL), double (MAX_REAL))
+ call dgsinit (sy1, GS_POLYNOMIAL, 2, 2, GS_XNONE, double (-MAX_REAL),
+ double (MAX_REAL), double (-MAX_REAL), double (MAX_REAL))
+$endif
+ sx2 = NULL
+ sy2 = NULL
+
+ # Get the magnification parameters.
+ xmag = clget$t ("xmag")
+ if (IS_$INDEF$T(xmag))
+ xmag = PIXEL(1.0)
+ ymag = clget$t ("ymag")
+ if (IS_$INDEF$T(ymag))
+ ymag = PIXEL(1.0)
+
+ # Get the rotation parameters.
+ xrot = clget$t ("xrot")
+ if (IS_$INDEF$T(xrot))
+ xrot = PIXEL(0.0)
+ xrot = -DEGTORAD(xrot)
+ yrot = clget$t ("yrot")
+ if (IS_$INDEF$T(yrot))
+ yrot = PIXEL(0.0)
+ yrot = -DEGTORAD(yrot)
+
+ # Set the magnification and rotation coefficients.
+ call geo_rotmag$t (sx1, sy1, xmag, ymag, xrot, yrot)
+
+ # Compute the origin of the reference coordinates.
+ xref = clget$t ("xref")
+ if (IS_$INDEF$T(xref))
+ xref = PIXEL(0.0)
+ yref = clget$t ("yref")
+ if (IS_$INDEF$T(yref))
+ yref = PIXEL(0.0)
+
+ # Compute the corresponding input coordinates.
+ xout = clget$t ("xout")
+ if (IS_$INDEF$T(xout))
+$if (datatype == r)
+ xout = gseval (sx1, xref, yref)
+$else
+ xout = dgseval (sx1, xref, yref)
+$endif
+ yout = clget$t ("yout")
+ if (IS_$INDEF$T(yout))
+$if (datatype == r)
+ yout = gseval (sy1, xref, yref)
+$else
+ yout = dgseval (sy1, xref, yref)
+$endif
+
+ # Set the shifts.
+ xshift = clget$t ("xshift")
+ yshift = clget$t ("yshift")
+$if (datatype == r)
+ if (IS_$INDEF$T(xshift))
+ xshift = xout - gseval (sx1, xref, yref)
+ if (IS_$INDEF$T(yshift))
+ yshift = yout - gseval (sy1, xref, yref)
+$else
+ if (IS_$INDEF$T(xshift))
+ xshift = xout - $tgseval (sx1, xref, yref)
+ if (IS_$INDEF$T(yshift))
+ yshift = yout - $tgseval (sy1, xref, yref)
+$endif
+ call geo_xyshift$t (sx1, sy1, xshift, yshift)
+end
+
+
+# GEO_SFREE -- Free the x and y surface fitting descriptors.
+
+$if (datatype == r)
+procedure geo_sfreer (sx1, sy1, sx2, sy2)
+$else
+procedure geo_sfreed (sx1, sy1, sx2, sy2)
+$endif
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+pointer sx2, sy2 #I/O pointer to the distortion x and y surfaces
+
+begin
+$if (datatype == r)
+ call gsfree (sx1)
+ call gsfree (sy1)
+ if (sx2 != NULL)
+ call gsfree (sx2)
+ if (sy2 != NULL)
+ call gsfree (sy2)
+$else
+ call dgsfree (sx1)
+ call dgsfree (sy1)
+ if (sx2 != NULL)
+ call dgsfree (sx2)
+ if (sy2 != NULL)
+ call dgsfree (sy2)
+$endif
+end
+
+
+# GEO_SINIT -- Read the surface fits from the database file and make
+# any requested changes.
+
+procedure geo_sinit$t (dt, record, geometry, sx1, sy1, sx2, sy2)
+
+pointer dt #I pointer to database file produced by geomap
+char record[ARB] #I the name of the database record
+int geometry #I the type of geometry to be computed
+pointer sx1, sy1 #O pointers to the linear x and y surfaces
+pointer sx2, sy2 #O pointers to the x and y distortion surfaces
+
+int i, rec, ncoeff, junk
+PIXEL xmag, ymag, xrot, yrot, xref, yref, xout, yout, xshift, yshift
+pointer newsx1, newsy1, xcoeff, ycoeff
+int dtlocate(), dtscan(), dtgeti()
+PIXEL clget$t()
+$if (datatype == r)
+errchk gsrestore
+$else
+errchk dgsrestore
+$endif
+
+begin
+ # Locate record.
+ rec = dtlocate (dt, record)
+
+ # Get linear part of fit.
+ ncoeff = dtgeti (dt, rec, "surface1")
+ call malloc (xcoeff, ncoeff, TY_PIXEL)
+ call malloc (ycoeff, ncoeff, TY_PIXEL)
+ do i = 1, ncoeff {
+ junk = dtscan (dt)
+ call garg$t (Mem$t[xcoeff+i-1])
+ call garg$t (Mem$t[ycoeff+i-1])
+ }
+
+ # Restore linear part of fit.
+$if (datatype == r)
+ call gsrestore (sx1, Mem$t[xcoeff])
+ call gsrestore (sy1, Mem$t[ycoeff])
+$else
+ call dgsrestore (sx1, Mem$t[xcoeff])
+ call dgsrestore (sy1, Mem$t[ycoeff])
+$endif
+
+ # Get geometric transformation.
+ xmag = clget$t ("xmag")
+ ymag = clget$t ("ymag")
+ xrot = clget$t ("xrotation")
+ yrot = clget$t ("yrotation")
+ xout = clget$t ("xout")
+ yout = clget$t ("yout")
+ xref = clget$t ("xref")
+ yref = clget$t ("yref")
+ xshift = clget$t ("xshift")
+ yshift = clget$t ("yshift")
+
+ # Get set to adjust linear part of the fit.
+$if (datatype == r)
+ call gscopy (sx1, newsx1)
+ call gscopy (sy1, newsy1)
+$else
+ call dgscopy (sx1, newsx1)
+ call dgscopy (sy1, newsy1)
+$endif
+
+ if (geometry == GEO_DISTORTION)
+ call geo_rotmag$t (newsx1, newsy1, PIXEL(1.0), PIXEL(1.0),
+ PIXEL(0.0), PIXEL(0.0))
+ else if (! IS_$INDEF$T(xmag) || ! IS_$INDEF$T(ymag) ||
+ ! IS_$INDEF$T(xrot) || ! IS_$INDEF$T(yrot))
+ call geo_drotmag$t (dt, rec, newsx1, newsy1, xmag, ymag,
+ xrot, yrot)
+ call geo_dxyshift$t (dt, rec, newsx1, newsy1, xout, yout, xref, yref,
+ xshift, yshift)
+$if (datatype == r)
+ call gssave (newsx1, Mem$t[xcoeff])
+ call gssave (newsy1, Mem$t[ycoeff])
+$else
+ call dgssave (newsx1, Mem$t[xcoeff])
+ call dgssave (newsy1, Mem$t[ycoeff])
+$endif
+
+ # Get distortion part of fit.
+ ncoeff = dtgeti (dt, rec, "surface2")
+ if (ncoeff > 0 && (geometry == GEO_GEOMETRIC ||
+ geometry == GEO_DISTORTION)) {
+
+ call realloc (xcoeff, ncoeff, TY_PIXEL)
+ call realloc (ycoeff, ncoeff, TY_PIXEL)
+ do i = 1, ncoeff {
+ junk = dtscan (dt)
+ call garg$t (Mem$t[xcoeff+i-1])
+ call garg$t (Mem$t[ycoeff+i-1])
+ }
+
+ # Restore distortion part of fit.
+$if (datatype == r)
+ iferr {
+ call gsrestore (sx2, Mem$t[xcoeff])
+ } then {
+ call mfree (sx2, TY_STRUCT)
+ sx2 = NULL
+ }
+ iferr {
+ call gsrestore (sy2, Mem$t[ycoeff])
+ } then {
+ call mfree (sy2, TY_STRUCT)
+ sy2 = NULL
+ }
+$else
+ iferr {
+ call dgsrestore (sx2, Mem$t[xcoeff])
+ } then {
+ call mfree (sx2, TY_STRUCT)
+ sx2 = NULL
+ }
+ iferr {
+ call dgsrestore (sy2, Mem$t[ycoeff])
+ } then {
+ call mfree (sy2, TY_STRUCT)
+ sy2 = NULL
+ }
+$endif
+
+ } else {
+ sx2 = NULL
+ sy2 = NULL
+ }
+
+ # Redefine the linear surfaces.
+$if (datatype == r)
+ call gsfree (sx1)
+ call gscopy (newsx1, sx1)
+ call gsfree (newsx1)
+ call gsfree (sy1)
+ call gscopy (newsy1, sy1)
+ call gsfree (newsy1)
+$else
+ call dgsfree (sx1)
+ call dgscopy (newsx1, sx1)
+ call dgsfree (newsx1)
+ call dgsfree (sy1)
+ call dgscopy (newsy1, sy1)
+ call dgsfree (newsy1)
+$endif
+
+ # Cleanup.
+ call mfree (xcoeff, TY_PIXEL)
+ call mfree (ycoeff, TY_PIXEL)
+end
+
+
+# GEO_DO_TRANSFORM -- The linear transformation is performed in this procedure.
+# First the coordinates are scaled, then rotated and translated. The
+# transformed coordinates are returned.
+
+procedure geo_do_transform$t (x, y, xt, yt, sx1, sy1, sx2, sy2)
+
+PIXEL x, y # initial positions
+PIXEL xt, yt # transformed positions
+pointer sx1, sy1 # pointer to linear surfaces
+pointer sx2, sy2 # pointer to distortion surfaces
+
+$if (datatype == r)
+real gseval()
+$else
+double dgseval()
+$endif
+
+begin
+$if (datatype == r)
+ xt = gseval (sx1, x, y)
+ if (sx2 != NULL)
+ xt = xt + gseval (sx2, x, y)
+ yt = gseval (sy1, x, y)
+ if (sy2 != NULL)
+ yt = yt + gseval (sy2, x, y)
+$else
+ xt = dgseval (sx1, x, y)
+ if (sx2 != NULL)
+ xt = xt + dgseval (sx2, x, y)
+ yt = dgseval (sy1, x, y)
+ if (sy2 != NULL)
+ yt = yt + dgseval (sy2, x, y)
+$endif
+end
+
+$endfor
diff --git a/pkg/images/immatch/src/geometry/geoxytran.x b/pkg/images/immatch/src/geometry/geoxytran.x
new file mode 100644
index 00000000..e8bb9f64
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/geoxytran.x
@@ -0,0 +1,446 @@
+include <ctype.h>
+include <mach.h>
+include <math.h>
+include <math/gsurfit.h>
+
+define GEO_LINEAR 1 # Linear transformation only
+define GEO_DISTORTION 2 # Distortion correction only
+define GEO_GEOMETRIC 3 # Full transformation
+
+
+
+# GEO_LINIT -- Initialize the linear part of the transformation.
+
+procedure geo_linitr (sx1, sy1, sx2, sy2)
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+pointer sx2, sy2 #I/O pointer to the distortion x and y surfaces
+
+real xmag, ymag, xrot, yrot, xref, yref, xout, yout, xshift, yshift
+real clgetr(), gseval()
+
+begin
+ # Initialize the surfaces.
+ call gsinit (sx1, GS_POLYNOMIAL, 2, 2, GS_XNONE, -MAX_REAL, MAX_REAL,
+ -MAX_REAL, MAX_REAL)
+ call gsinit (sy1, GS_POLYNOMIAL, 2, 2, GS_XNONE, -MAX_REAL, MAX_REAL,
+ -MAX_REAL, MAX_REAL)
+ sx2 = NULL
+ sy2 = NULL
+
+ # Get the magnification parameters.
+ xmag = clgetr ("xmag")
+ if (IS_INDEFR(xmag))
+ xmag = real(1.0)
+ ymag = clgetr ("ymag")
+ if (IS_INDEFR(ymag))
+ ymag = real(1.0)
+
+ # Get the rotation parameters.
+ xrot = clgetr ("xrot")
+ if (IS_INDEFR(xrot))
+ xrot = real(0.0)
+ xrot = -DEGTORAD(xrot)
+ yrot = clgetr ("yrot")
+ if (IS_INDEFR(yrot))
+ yrot = real(0.0)
+ yrot = -DEGTORAD(yrot)
+
+ # Set the magnification and rotation coefficients.
+ call geo_rotmagr (sx1, sy1, xmag, ymag, xrot, yrot)
+
+ # Compute the origin of the reference coordinates.
+ xref = clgetr ("xref")
+ if (IS_INDEFR(xref))
+ xref = real(0.0)
+ yref = clgetr ("yref")
+ if (IS_INDEFR(yref))
+ yref = real(0.0)
+
+ # Compute the corresponding input coordinates.
+ xout = clgetr ("xout")
+ if (IS_INDEFR(xout))
+ xout = gseval (sx1, xref, yref)
+ yout = clgetr ("yout")
+ if (IS_INDEFR(yout))
+ yout = gseval (sy1, xref, yref)
+
+ # Set the shifts.
+ xshift = clgetr ("xshift")
+ yshift = clgetr ("yshift")
+ if (IS_INDEFR(xshift))
+ xshift = xout - gseval (sx1, xref, yref)
+ if (IS_INDEFR(yshift))
+ yshift = yout - gseval (sy1, xref, yref)
+ call geo_xyshiftr (sx1, sy1, xshift, yshift)
+end
+
+
+# GEO_SFREE -- Free the x and y surface fitting descriptors.
+
+procedure geo_sfreer (sx1, sy1, sx2, sy2)
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+pointer sx2, sy2 #I/O pointer to the distortion x and y surfaces
+
+begin
+ call gsfree (sx1)
+ call gsfree (sy1)
+ if (sx2 != NULL)
+ call gsfree (sx2)
+ if (sy2 != NULL)
+ call gsfree (sy2)
+end
+
+
+# GEO_SINIT -- Read the surface fits from the database file and make
+# any requested changes.
+
+procedure geo_sinitr (dt, record, geometry, sx1, sy1, sx2, sy2)
+
+pointer dt #I pointer to database file produced by geomap
+char record[ARB] #I the name of the database record
+int geometry #I the type of geometry to be computed
+pointer sx1, sy1 #O pointers to the linear x and y surfaces
+pointer sx2, sy2 #O pointers to the x and y distortion surfaces
+
+int i, rec, ncoeff, junk
+real xmag, ymag, xrot, yrot, xref, yref, xout, yout, xshift, yshift
+pointer newsx1, newsy1, xcoeff, ycoeff
+int dtlocate(), dtscan(), dtgeti()
+real clgetr()
+errchk gsrestore
+
+begin
+ # Locate record.
+ rec = dtlocate (dt, record)
+
+ # Get linear part of fit.
+ ncoeff = dtgeti (dt, rec, "surface1")
+ call malloc (xcoeff, ncoeff, TY_REAL)
+ call malloc (ycoeff, ncoeff, TY_REAL)
+ do i = 1, ncoeff {
+ junk = dtscan (dt)
+ call gargr (Memr[xcoeff+i-1])
+ call gargr (Memr[ycoeff+i-1])
+ }
+
+ # Restore linear part of fit.
+ call gsrestore (sx1, Memr[xcoeff])
+ call gsrestore (sy1, Memr[ycoeff])
+
+ # Get geometric transformation.
+ xmag = clgetr ("xmag")
+ ymag = clgetr ("ymag")
+ xrot = clgetr ("xrotation")
+ yrot = clgetr ("yrotation")
+ xout = clgetr ("xout")
+ yout = clgetr ("yout")
+ xref = clgetr ("xref")
+ yref = clgetr ("yref")
+ xshift = clgetr ("xshift")
+ yshift = clgetr ("yshift")
+
+ # Get set to adjust linear part of the fit.
+ call gscopy (sx1, newsx1)
+ call gscopy (sy1, newsy1)
+
+ if (geometry == GEO_DISTORTION)
+ call geo_rotmagr (newsx1, newsy1, real(1.0), real(1.0),
+ real(0.0), real(0.0))
+ else if (! IS_INDEFR(xmag) || ! IS_INDEFR(ymag) ||
+ ! IS_INDEFR(xrot) || ! IS_INDEFR(yrot))
+ call geo_drotmagr (dt, rec, newsx1, newsy1, xmag, ymag,
+ xrot, yrot)
+ call geo_dxyshiftr (dt, rec, newsx1, newsy1, xout, yout, xref, yref,
+ xshift, yshift)
+ call gssave (newsx1, Memr[xcoeff])
+ call gssave (newsy1, Memr[ycoeff])
+
+ # Get distortion part of fit.
+ ncoeff = dtgeti (dt, rec, "surface2")
+ if (ncoeff > 0 && (geometry == GEO_GEOMETRIC ||
+ geometry == GEO_DISTORTION)) {
+
+ call realloc (xcoeff, ncoeff, TY_REAL)
+ call realloc (ycoeff, ncoeff, TY_REAL)
+ do i = 1, ncoeff {
+ junk = dtscan (dt)
+ call gargr (Memr[xcoeff+i-1])
+ call gargr (Memr[ycoeff+i-1])
+ }
+
+ # Restore distortion part of fit.
+ iferr {
+ call gsrestore (sx2, Memr[xcoeff])
+ } then {
+ call mfree (sx2, TY_STRUCT)
+ sx2 = NULL
+ }
+ iferr {
+ call gsrestore (sy2, Memr[ycoeff])
+ } then {
+ call mfree (sy2, TY_STRUCT)
+ sy2 = NULL
+ }
+
+ } else {
+ sx2 = NULL
+ sy2 = NULL
+ }
+
+ # Redefine the linear surfaces.
+ call gsfree (sx1)
+ call gscopy (newsx1, sx1)
+ call gsfree (newsx1)
+ call gsfree (sy1)
+ call gscopy (newsy1, sy1)
+ call gsfree (newsy1)
+
+ # Cleanup.
+ call mfree (xcoeff, TY_REAL)
+ call mfree (ycoeff, TY_REAL)
+end
+
+
+# GEO_DO_TRANSFORM -- The linear transformation is performed in this procedure.
+# First the coordinates are scaled, then rotated and translated. The
+# transformed coordinates are returned.
+
+procedure geo_do_transformr (x, y, xt, yt, sx1, sy1, sx2, sy2)
+
+real x, y # initial positions
+real xt, yt # transformed positions
+pointer sx1, sy1 # pointer to linear surfaces
+pointer sx2, sy2 # pointer to distortion surfaces
+
+real gseval()
+
+begin
+ xt = gseval (sx1, x, y)
+ if (sx2 != NULL)
+ xt = xt + gseval (sx2, x, y)
+ yt = gseval (sy1, x, y)
+ if (sy2 != NULL)
+ yt = yt + gseval (sy2, x, y)
+end
+
+
+
+# GEO_LINIT -- Initialize the linear part of the transformation.
+
+procedure geo_linitd (sx1, sy1, sx2, sy2)
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+pointer sx2, sy2 #I/O pointer to the distortion x and y surfaces
+
+double xmag, ymag, xrot, yrot, xref, yref, xout, yout, xshift, yshift
+double clgetd(), dgseval()
+
+begin
+ # Initialize the surfaces.
+ call dgsinit (sx1, GS_POLYNOMIAL, 2, 2, GS_XNONE, double (-MAX_REAL),
+ double (MAX_REAL), double (-MAX_REAL), double (MAX_REAL))
+ call dgsinit (sy1, GS_POLYNOMIAL, 2, 2, GS_XNONE, double (-MAX_REAL),
+ double (MAX_REAL), double (-MAX_REAL), double (MAX_REAL))
+ sx2 = NULL
+ sy2 = NULL
+
+ # Get the magnification parameters.
+ xmag = clgetd ("xmag")
+ if (IS_INDEFD(xmag))
+ xmag = double(1.0)
+ ymag = clgetd ("ymag")
+ if (IS_INDEFD(ymag))
+ ymag = double(1.0)
+
+ # Get the rotation parameters.
+ xrot = clgetd ("xrot")
+ if (IS_INDEFD(xrot))
+ xrot = double(0.0)
+ xrot = -DEGTORAD(xrot)
+ yrot = clgetd ("yrot")
+ if (IS_INDEFD(yrot))
+ yrot = double(0.0)
+ yrot = -DEGTORAD(yrot)
+
+ # Set the magnification and rotation coefficients.
+ call geo_rotmagd (sx1, sy1, xmag, ymag, xrot, yrot)
+
+ # Compute the origin of the reference coordinates.
+ xref = clgetd ("xref")
+ if (IS_INDEFD(xref))
+ xref = double(0.0)
+ yref = clgetd ("yref")
+ if (IS_INDEFD(yref))
+ yref = double(0.0)
+
+ # Compute the corresponding input coordinates.
+ xout = clgetd ("xout")
+ if (IS_INDEFD(xout))
+ xout = dgseval (sx1, xref, yref)
+ yout = clgetd ("yout")
+ if (IS_INDEFD(yout))
+ yout = dgseval (sy1, xref, yref)
+
+ # Set the shifts.
+ xshift = clgetd ("xshift")
+ yshift = clgetd ("yshift")
+ if (IS_INDEFD(xshift))
+ xshift = xout - dgseval (sx1, xref, yref)
+ if (IS_INDEFD(yshift))
+ yshift = yout - dgseval (sy1, xref, yref)
+ call geo_xyshiftd (sx1, sy1, xshift, yshift)
+end
+
+
+# GEO_SFREE -- Free the x and y surface fitting descriptors.
+
+procedure geo_sfreed (sx1, sy1, sx2, sy2)
+
+pointer sx1, sy1 #I/O pointers to the linear x and y surfaces
+pointer sx2, sy2 #I/O pointer to the distortion x and y surfaces
+
+begin
+ call dgsfree (sx1)
+ call dgsfree (sy1)
+ if (sx2 != NULL)
+ call dgsfree (sx2)
+ if (sy2 != NULL)
+ call dgsfree (sy2)
+end
+
+
+# GEO_SINIT -- Read the surface fits from the database file and make
+# any requested changes.
+
+procedure geo_sinitd (dt, record, geometry, sx1, sy1, sx2, sy2)
+
+pointer dt #I pointer to database file produced by geomap
+char record[ARB] #I the name of the database record
+int geometry #I the type of geometry to be computed
+pointer sx1, sy1 #O pointers to the linear x and y surfaces
+pointer sx2, sy2 #O pointers to the x and y distortion surfaces
+
+int i, rec, ncoeff, junk
+double xmag, ymag, xrot, yrot, xref, yref, xout, yout, xshift, yshift
+pointer newsx1, newsy1, xcoeff, ycoeff
+int dtlocate(), dtscan(), dtgeti()
+double clgetd()
+errchk dgsrestore
+
+begin
+ # Locate record.
+ rec = dtlocate (dt, record)
+
+ # Get linear part of fit.
+ ncoeff = dtgeti (dt, rec, "surface1")
+ call malloc (xcoeff, ncoeff, TY_DOUBLE)
+ call malloc (ycoeff, ncoeff, TY_DOUBLE)
+ do i = 1, ncoeff {
+ junk = dtscan (dt)
+ call gargd (Memd[xcoeff+i-1])
+ call gargd (Memd[ycoeff+i-1])
+ }
+
+ # Restore linear part of fit.
+ call dgsrestore (sx1, Memd[xcoeff])
+ call dgsrestore (sy1, Memd[ycoeff])
+
+ # Get geometric transformation.
+ xmag = clgetd ("xmag")
+ ymag = clgetd ("ymag")
+ xrot = clgetd ("xrotation")
+ yrot = clgetd ("yrotation")
+ xout = clgetd ("xout")
+ yout = clgetd ("yout")
+ xref = clgetd ("xref")
+ yref = clgetd ("yref")
+ xshift = clgetd ("xshift")
+ yshift = clgetd ("yshift")
+
+ # Get set to adjust linear part of the fit.
+ call dgscopy (sx1, newsx1)
+ call dgscopy (sy1, newsy1)
+
+ if (geometry == GEO_DISTORTION)
+ call geo_rotmagd (newsx1, newsy1, double(1.0), double(1.0),
+ double(0.0), double(0.0))
+ else if (! IS_INDEFD(xmag) || ! IS_INDEFD(ymag) ||
+ ! IS_INDEFD(xrot) || ! IS_INDEFD(yrot))
+ call geo_drotmagd (dt, rec, newsx1, newsy1, xmag, ymag,
+ xrot, yrot)
+ call geo_dxyshiftd (dt, rec, newsx1, newsy1, xout, yout, xref, yref,
+ xshift, yshift)
+ call dgssave (newsx1, Memd[xcoeff])
+ call dgssave (newsy1, Memd[ycoeff])
+
+ # Get distortion part of fit.
+ ncoeff = dtgeti (dt, rec, "surface2")
+ if (ncoeff > 0 && (geometry == GEO_GEOMETRIC ||
+ geometry == GEO_DISTORTION)) {
+
+ call realloc (xcoeff, ncoeff, TY_DOUBLE)
+ call realloc (ycoeff, ncoeff, TY_DOUBLE)
+ do i = 1, ncoeff {
+ junk = dtscan (dt)
+ call gargd (Memd[xcoeff+i-1])
+ call gargd (Memd[ycoeff+i-1])
+ }
+
+ # Restore distortion part of fit.
+ iferr {
+ call dgsrestore (sx2, Memd[xcoeff])
+ } then {
+ call mfree (sx2, TY_STRUCT)
+ sx2 = NULL
+ }
+ iferr {
+ call dgsrestore (sy2, Memd[ycoeff])
+ } then {
+ call mfree (sy2, TY_STRUCT)
+ sy2 = NULL
+ }
+
+ } else {
+ sx2 = NULL
+ sy2 = NULL
+ }
+
+ # Redefine the linear surfaces.
+ call dgsfree (sx1)
+ call dgscopy (newsx1, sx1)
+ call dgsfree (newsx1)
+ call dgsfree (sy1)
+ call dgscopy (newsy1, sy1)
+ call dgsfree (newsy1)
+
+ # Cleanup.
+ call mfree (xcoeff, TY_DOUBLE)
+ call mfree (ycoeff, TY_DOUBLE)
+end
+
+
+# GEO_DO_TRANSFORM -- The linear transformation is performed in this procedure.
+# First the coordinates are scaled, then rotated and translated. The
+# transformed coordinates are returned.
+
+procedure geo_do_transformd (x, y, xt, yt, sx1, sy1, sx2, sy2)
+
+double x, y # initial positions
+double xt, yt # transformed positions
+pointer sx1, sy1 # pointer to linear surfaces
+pointer sx2, sy2 # pointer to distortion surfaces
+
+double dgseval()
+
+begin
+ xt = dgseval (sx1, x, y)
+ if (sx2 != NULL)
+ xt = xt + dgseval (sx2, x, y)
+ yt = dgseval (sy1, x, y)
+ if (sy2 != NULL)
+ yt = yt + dgseval (sy2, x, y)
+end
+
+
diff --git a/pkg/images/immatch/src/geometry/mkpkg b/pkg/images/immatch/src/geometry/mkpkg
new file mode 100644
index 00000000..e6e98b24
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/mkpkg
@@ -0,0 +1,34 @@
+# Make the GEOMAP/GEOXYTRAN and CCMAP/CCSETWCS/CCTRAN tasks
+
+$checkout libpkg.a ../../../
+$update libpkg.a
+$checkin libpkg.a ../../../
+$exit
+
+generic:
+ $set GEN = "$$generic -k"
+
+ $ifolder (geofunc.x, geofunc.gx)
+ $(GEN) geofunc.gx -o geofunc.x $endif
+ $ifolder (t_geomap.x, t_geomap.gx)
+ $(GEN) t_geomap.gx -o t_geomap.x $endif
+ $ifolder (geoxytran.x,geoxytran.gx)
+ $(GEN) geoxytran.gx -o geoxytran.x $endif
+ ;
+
+libpkg.a:
+ $ifeq (USE_GENERIC, yes) $call generic $endif
+
+ geofunc.x <math.h> <math/gsurfit.h>
+ geotimtran.x <imhdr.h> <imset.h> <mach.h> <math/gsurfit.h> \
+ <math/iminterp.h> geotran.h
+ geotran.x <imhdr.h> <imset.h> <mach.h> <math/gsurfit.h> \
+ <math/iminterp.h> geotran.h
+ geoxytran.x <mach.h> <ctype.h> <math.h> <math/gsurfit.h>
+ t_geomap.x <fset.h> <error.h> <mach.h> <math/gsurfit.h> \
+ <math.h> "../../../lib/geomap.h"
+ t_geotran.x <imhdr.h> <mwset.h> <math.h> <math/gsurfit.h> \
+ geotran.h
+ t_geoxytran.x <fset.h> <ctype.h>
+ trinvert.x
+ ;
diff --git a/pkg/images/immatch/src/geometry/t_geomap.gx b/pkg/images/immatch/src/geometry/t_geomap.gx
new file mode 100644
index 00000000..02d530e5
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/t_geomap.gx
@@ -0,0 +1,921 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+include <error.h>
+include <mach.h>
+include <math.h>
+include <math/gsurfit.h>
+include "../../../lib/geomap.h"
+
+define GM_REAL 1 # computation type is real
+define GM_DOUBLE 2 # computation type is double
+
+$for (r)
+
+# T_GEOMAP -- Procedure to calculate the transformation required to transform
+# the coordinate system of a reference image to the coordinate system of
+# an input image. The transformation is of the following form.
+#
+# xin = f (xref, yref)
+# yin = g (xref, yref)
+
+procedure t_geomap ()
+
+bool verbose, interactive
+double xmin, xmax, ymin, ymax, reject
+int geometry, function, calctype, nfiles, list, in, reclist, nrecords
+int xxorder, xyorder, xxterms, yxorder, yyorder, yxterms, maxiter
+int reslist, nresfiles, res
+pointer sp, in_name, str, out, fit, gd, graphics
+real rxmin, rxmax, rymin, rymax
+
+bool clgetb()
+double clgetd()
+int clgeti(), clgwrd(), clplen(), errget(), imtopenp(), imtlen()
+int imtgetim()
+pointer clpopnu(), clgfil(), dtmap(), gopen(), open()
+
+errchk geo_mapr(), geo_mapd()
+
+begin
+ # Get working space.
+ call smark (sp)
+ call salloc (in_name, SZ_FNAME, TY_CHAR)
+ call salloc (graphics, SZ_FNAME, TY_CHAR)
+ call salloc (str, max(SZ_LINE, SZ_FNAME), TY_CHAR)
+
+ # Get input data file(s).
+ list = clpopnu ("input")
+ nfiles = clplen (list)
+
+ # Open database output file.
+ call clgstr ("database", Memc[str], SZ_FNAME)
+ out = dtmap (Memc[str], APPEND)
+
+ # Get minimum and maximum reference values.
+ xmin = clgetd ("xmin")
+ if (IS_INDEFD(xmin))
+ rxmin = INDEFR
+ else
+ rxmin = xmin
+ xmax = clgetd ("xmax")
+ if (IS_INDEFD(xmax))
+ rxmax = INDEFR
+ else
+ rxmax = xmax
+ ymin = clgetd ("ymin")
+ if (IS_INDEFD(ymin))
+ rymin = INDEFR
+ else
+ rymin = ymin
+ ymax = clgetd ("ymax")
+ if (IS_INDEFD(ymax))
+ rymax = INDEFR
+ else
+ rymax = ymax
+
+ # Get the records list.
+ reclist = imtopenp ("transforms")
+ nrecords = imtlen (reclist)
+ if ((nrecords > 0) && (nrecords != nfiles)) {
+ call eprintf (
+ "The number of records is not equal to the number of input files")
+ call clpcls (list)
+ call dtunmap (out)
+ call imtclose (reclist)
+ call sfree (sp)
+ return
+ }
+
+ # Get the results file list.
+ reslist = clpopnu ("results")
+ nresfiles = clplen (reslist)
+ if (nresfiles > 1 && nresfiles != nfiles) {
+ call eprintf ("Error: there are too few results files\n")
+ call clpcls (list)
+ call dtunmap (out)
+ call imtclose (reclist)
+ call clpcls (reslist)
+ call sfree (sp)
+ return
+ }
+
+ # Get the surface fitting parameters.
+ geometry = clgwrd ("fitgeometry", Memc[str], SZ_LINE, GM_GEOMETRIES)
+ function = clgwrd ("function", Memc[str], SZ_LINE, GM_FUNCS)
+ xxorder = clgeti ("xxorder")
+ xyorder = clgeti ("xyorder")
+ xxterms = clgwrd ("xxterms", Memc[str], SZ_LINE, GM_XFUNCS) - 1
+ yxorder = clgeti ("yxorder")
+ yyorder = clgeti ("yyorder")
+ yxterms = clgwrd ("yxterms", Memc[str], SZ_LINE, GM_XFUNCS) - 1
+ maxiter = clgeti ("maxiter")
+ reject = clgetd ("reject")
+ calctype = clgwrd ("calctype", Memc[str], SZ_LINE, ",real,double,")
+
+ # Get the graphics parameters.
+ verbose = clgetb ("verbose")
+ interactive = clgetb ("interactive")
+ call clgstr ("graphics", Memc[graphics], SZ_FNAME)
+
+ # Flush standard output on newline.
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Initialize the fit structure.
+ call geo_minit (fit, GM_NONE, geometry, function, xxorder, xyorder,
+ xxterms, yxorder, yyorder, yxterms, maxiter, reject)
+
+ # Loop over the files.
+ while (clgfil (list, Memc[in_name], SZ_FNAME) != EOF) {
+
+ # Open text file of coordinates.
+ in = open (Memc[in_name], READ_ONLY, TEXT_FILE)
+
+ # Open the results files.
+ if (nresfiles <= 0)
+ res = NULL
+ else if (clgfil (reslist, Memc[str], SZ_FNAME) != EOF)
+ res = open (Memc[str], NEW_FILE, TEXT_FILE)
+
+ # Set file name in structure.
+ if (nrecords > 0) {
+ if (imtgetim (reclist, GM_RECORD(fit), SZ_FNAME) != EOF)
+ ;
+ } else
+ call strcpy (Memc[in_name], GM_RECORD(fit), SZ_FNAME)
+
+ if (verbose && res != STDOUT) {
+ call fstats (in, F_FILENAME, Memc[str], SZ_FNAME)
+ call printf ("\nCoordinate list: %s Transform: %s\n")
+ call pargstr (Memc[str])
+ call pargstr (GM_RECORD(fit))
+ if (res != NULL)
+ call fstats (res, F_FILENAME, Memc[str], SZ_FNAME)
+ else
+ call strcpy ("", Memc[str], SZ_FNAME)
+ call printf (" Results file: %s\n")
+ call pargstr (Memc[str])
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fstats (in, F_FILENAME, Memc[str], SZ_FNAME)
+ call fprintf (res, "\n# Coordinate list: %s Transform: %s\n")
+ call pargstr (Memc[str])
+ call pargstr (GM_RECORD(fit))
+ if (res != NULL)
+ call fstats (res, F_FILENAME, Memc[str], SZ_FNAME)
+ else
+ call strcpy ("", Memc[str], SZ_FNAME)
+ call fprintf (res, "# Results file: %s\n")
+ call pargstr (Memc[str])
+ call flush (STDOUT)
+ }
+
+ if (interactive) {
+ gd = gopen (Memc[graphics], NEW_FILE, STDGRAPH)
+ } else
+ gd = NULL
+
+ iferr {
+ if (calctype == GM_REAL)
+ call geo_mapr (gd, in, out, res, fit, rxmin, rxmax, rymin,
+ rymax, verbose)
+ else
+ call geo_mapd (gd, in, out, res, fit, xmin, xmax, ymin,
+ ymax, verbose)
+ } then {
+ if (verbose && res != STDOUT) {
+ call printf ("Error fitting coordinate list: %s\n")
+ call pargstr (Memc[in_name])
+ call flush (STDOUT)
+ if (errget (Memc[str], SZ_LINE) == 0)
+ ;
+ call printf ("\t%s\n")
+ call pargstr (Memc[str))
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Error fitting coordinate list: %s\n")
+ call pargstr (Memc[in_name])
+ call flush (STDOUT)
+ if (errget (Memc[str], SZ_LINE) == 0)
+ ;
+ call fprintf (res, "# %s\n")
+ call pargstr (Memc[str))
+ }
+ }
+
+ call close (in)
+ if (nresfiles == nfiles)
+ call close ( res)
+
+ if (gd != NULL)
+ call gclose (gd)
+ }
+
+ # Close up.
+ call geo_free (fit)
+ if (nresfiles < nfiles)
+ call close ( res)
+ call dtunmap (out)
+ call imtclose (reclist)
+ call clpcls (list)
+ call sfree (sp)
+end
+
+$endfor
+
+$for (rd)
+
+# GEO_MAP -- Procedure to calculate the coordinate transformations
+
+procedure geo_map$t (gd, in, out, res, fit, xmin, xmax, ymin, ymax, verbose)
+
+pointer gd #I the graphics stream
+int in #I the input file descriptor
+pointer out #I the output file descriptor
+int res #I the results file descriptor
+pointer fit #I pointer to fit parameters
+PIXEL xmin, xmax #I max and min xref values
+PIXEL ymin, ymax #I max and min yref values
+bool verbose #I verbose mode
+
+int npts, ngood
+pointer sp, str, xref, yref, xin, yin, wts, xfit, yfit, xerrmsg, yerrmsg
+pointer sx1, sy1, sx2, sy2
+PIXEL mintemp, maxtemp
+
+PIXEL asum$t()
+int geo_rdxy$t()
+errchk geo_fit$t, geo_mgfit$t()
+
+begin
+ # Get working space.
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+ call salloc (xerrmsg, SZ_LINE, TY_CHAR)
+ call salloc (yerrmsg, SZ_LINE, TY_CHAR)
+
+ # Initialize pointers.
+ xref = NULL
+ yref = NULL
+ xin = NULL
+ yin = NULL
+ wts = NULL
+
+ # Read in data and check that data is in range.
+ npts = geo_rdxy$t (in, xref, yref, xin, yin, xmin, xmax, ymin, ymax)
+ if (npts <= 0) {
+ call fstats (in, F_FILENAME, Memc[str], SZ_FNAME)
+ call printf ("Coordinate list: %s has no data in range.\n")
+ call pargstr (Memc[str])
+ call sfree (sp)
+ return
+ }
+
+ # Compute the mean of the reference and input coordinates.
+ GM_XOREF(fit) = double (asum$t (Mem$t[xref], npts) / npts)
+ GM_YOREF(fit) = double (asum$t (Mem$t[yref], npts) / npts)
+ GM_XOIN(fit) = double (asum$t (Mem$t[xin], npts) / npts)
+ GM_YOIN(fit) = double (asum$t (Mem$t[yin], npts) / npts)
+
+ # Set the reference point for the projections to INDEF.
+ GM_XREFPT(fit) = INDEFD
+ GM_YREFPT(fit) = INDEFD
+
+ # Compute the weights.
+ call malloc (xfit, npts, TY_PIXEL)
+ call malloc (yfit, npts, TY_PIXEL)
+ call malloc (wts, npts, TY_PIXEL)
+ call amovk$t (PIXEL(1.), Mem$t[wts], npts)
+
+ # Determine the x max and min.
+ if (IS_$INDEF$T(xmin) || IS_$INDEF$T(xmax)) {
+ call alim$t (Mem$t[xref], npts, mintemp, maxtemp)
+ if (! IS_$INDEF$T(xmin))
+ GM_XMIN(fit) = double (xmin)
+ else
+ GM_XMIN(fit) = double (mintemp)
+ if (! IS_$INDEF$T(xmax))
+ GM_XMAX(fit) = double (xmax)
+ else
+ GM_XMAX(fit) = double (maxtemp)
+ } else {
+ GM_XMIN(fit) = double (xmin)
+ GM_XMAX(fit) = double (xmax)
+ }
+
+ # Determine the y max and min.
+ if (IS_$INDEF$T(ymin) || IS_$INDEF$T(ymax)) {
+ call alim$t (Mem$t[yref], npts, mintemp, maxtemp)
+ if (! IS_$INDEF$T(ymin))
+ GM_YMIN(fit) = double (ymin)
+ else
+ GM_YMIN(fit) = double (mintemp)
+ if (! IS_$INDEF$T(ymax))
+ GM_YMAX(fit) = double (ymax)
+ else
+ GM_YMAX(fit) = double (maxtemp)
+ } else {
+ GM_YMIN(fit) = double (ymin)
+ GM_YMAX(fit) = double (ymax)
+ }
+
+ # Initalize surface pointers.
+ sx1 = NULL
+ sy1 = NULL
+ sx2 = NULL
+ sy2 = NULL
+
+ # Fit the data.
+ if (gd != NULL) {
+ iferr {
+ call geo_mgfit$t (gd, fit, sx1, sy1, sx2, sy2, Mem$t[xref],
+ Mem$t[yref], Mem$t[xin], Mem$t[yin], Mem$t[wts], npts,
+ Memc[xerrmsg], Memc[yerrmsg], SZ_LINE)
+ } then {
+ call gdeactivate (gd, 0)
+ call mfree (xfit, TY_PIXEL)
+ call mfree (yfit, TY_PIXEL)
+ call mfree (wts, TY_PIXEL)
+ call geo_mmfree$t (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+ call error (3, "Too few points for X or Y fits.")
+ }
+ call gdeactivate (gd, 0)
+ if (verbose && res != STDOUT) {
+ call printf ("Coordinate mapping status\n")
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Coordinate mapping status\n")
+ }
+ } else {
+ if (verbose && res != STDOUT) {
+ call printf ("Coordinate mapping status\n ")
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Coordinate mapping status\n# ")
+ }
+ iferr {
+ call geo_fit$t (fit, sx1, sy1, sx2, sy2, Mem$t[xref],
+ Mem$t[yref], Mem$t[xin], Mem$t[yin], Mem$t[wts], npts,
+ Memc[xerrmsg], Memc[yerrmsg], SZ_LINE)
+ } then {
+ call mfree (xfit, TY_PIXEL)
+ call mfree (yfit, TY_PIXEL)
+ call mfree (wts, TY_PIXEL)
+ call geo_mmfree$t (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+ call error (3, "Too few points for X or Y fits.")
+ }
+ if (verbose && res != STDOUT) {
+ call printf ("%s %s\n")
+ call pargstr (Memc[xerrmsg])
+ call pargstr (Memc[yerrmsg])
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "%s %s\n")
+ call pargstr (Memc[xerrmsg])
+ call pargstr (Memc[yerrmsg])
+ call flush (STDOUT)
+ }
+ }
+ ngood = GM_NPTS(fit) - GM_NWTS0(fit)
+ if (verbose && res != STDOUT) {
+ call printf (" Xin and Yin fit rms: %0.7g %0.7g\n")
+ if (ngood <= 1) {
+ call pargd (0.0d0)
+ call pargd (0.0d0)
+ } else {
+ call pargd (sqrt (GM_XRMS(fit) / (ngood - 1)))
+ call pargd (sqrt (GM_YRMS(fit) / (ngood - 1)))
+ }
+ call geo_show$t (STDOUT, fit, sx1, sy1, NO)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Xin and Yin fit rms: %0.7g %0.7g\n")
+ if (ngood <= 1) {
+ call pargd (0.0)
+ call pargd (0.0)
+ } else {
+ call pargd (sqrt (GM_XRMS(fit) / (ngood - 1)))
+ call pargd (sqrt (GM_YRMS(fit) / (ngood - 1)))
+ }
+ call geo_show$t (res, fit, sx1, sy1, YES)
+ }
+
+ # Compute and print the fitted x and y values.
+ if (res != NULL) {
+ call geo_eval$t (sx1, sy1, sx2, sy2, Mem$t[xref], Mem$t[yref],
+ Mem$t[xfit], Mem$t[yfit], npts)
+ call geo_plist$t (res, fit, Mem$t[xref], Mem$t[yref], Mem$t[xin],
+ Mem$t[yin], Mem$t[xfit], Mem$t[yfit], Mem$t[wts], npts)
+ }
+
+ # Free the data
+ if (xref != NULL)
+ call mfree (xref, TY_PIXEL)
+ if (yref != NULL)
+ call mfree (yref, TY_PIXEL)
+ if (xin != NULL)
+ call mfree (xin, TY_PIXEL)
+ if (yin != NULL)
+ call mfree (yin, TY_PIXEL)
+ if (xfit != NULL)
+ call mfree (xfit, TY_PIXEL)
+ if (yfit != NULL)
+ call mfree (yfit, TY_PIXEL)
+ if (wts != NULL)
+ call mfree (wts, TY_PIXEL)
+
+ # Output the data.
+ call geo_mout$t (fit, out, sx1, sy1, sx2, sy2)
+
+ # Free the space and close files.
+ call geo_mmfree$t (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+end
+
+
+define GEO_DEFBUFSIZE 1000 # default data buffer sizes
+
+# GEO_RDXY -- Read in the data points.
+
+int procedure geo_rdxy$t (fd, xref, yref, xin, yin, xmin, xmax, ymin, ymax)
+
+int fd # the input file descriptor
+pointer xref # the x reference coordinates
+pointer yref # the y reference coordinates
+pointer xin # the x coordinates
+pointer yin # the y coordinates
+PIXEL xmin, xmax # the range of the x coordinates
+PIXEL ymin, ymax # the range of the y coordinates
+
+int npts, bufsize
+int fscan(), nscan()
+
+begin
+ bufsize = GEO_DEFBUFSIZE
+ call malloc (xref, bufsize, TY_PIXEL)
+ call malloc (yref, bufsize, TY_PIXEL)
+ call malloc (xin, bufsize, TY_PIXEL)
+ call malloc (yin, bufsize, TY_PIXEL)
+
+ npts = 0
+ while (fscan (fd) != EOF) {
+
+ # Decode the data.
+ call garg$t (Mem$t[xref+npts])
+ call garg$t (Mem$t[yref+npts])
+ call garg$t (Mem$t[xin+npts])
+ call garg$t (Mem$t[yin+npts])
+ if (nscan() < 4)
+ next
+
+ # Check the data limits.
+ if (! IS_$INDEF$T(xmin)) {
+ if (Mem$t[xref+npts] < xmin)
+ next
+ }
+ if (! IS_$INDEF$T(xmax)) {
+ if (Mem$t[xref+npts] > xmax)
+ next
+ }
+ if (! IS_$INDEF$T(ymin)) {
+ if (Mem$t[yref+npts] < ymin)
+ next
+ }
+ if (! IS_$INDEF$T(ymax)) {
+ if (Mem$t[yref+npts] > ymax)
+ next
+ }
+
+ npts = npts + 1
+ if (npts >= bufsize) {
+ bufsize = bufsize + GEO_DEFBUFSIZE
+ call realloc (xref, bufsize, TY_PIXEL)
+ call realloc (yref, bufsize, TY_PIXEL)
+ call realloc (xin, bufsize, TY_PIXEL)
+ call realloc (yin, bufsize, TY_PIXEL)
+ }
+ }
+
+ if (npts <= 0) {
+ call mfree (xref, TY_PIXEL)
+ call mfree (yref, TY_PIXEL)
+ call mfree (xin, TY_PIXEL)
+ call mfree (yin, TY_PIXEL)
+ xref = NULL
+ yref = NULL
+ xin = NULL
+ yin = NULL
+ } else if (npts < bufsize) {
+ call realloc (xref, npts, TY_PIXEL)
+ call realloc (yref, npts, TY_PIXEL)
+ call realloc (xin, npts, TY_PIXEL)
+ call realloc (yin, npts, TY_PIXEL)
+ }
+
+ return (npts)
+end
+
+
+# GEO_EVAL -- Evalute the fit.
+
+procedure geo_eval$t (sx1, sy1, sx2, sy2, xref, yref, xi, eta, npts)
+
+pointer sx1, sy1 #I pointer to linear surfaces
+pointer sx2, sy2 #I pointer to higher order surfaces
+PIXEL xref[ARB] #I the x reference coordinates
+PIXEL yref[ARB] #I the y reference coordinates
+PIXEL xi[ARB] #O the fitted xi coordinates
+PIXEL eta[ARB] #O the fitted eta coordinates
+int npts #I the number of points
+
+pointer sp, temp
+
+begin
+ call smark (sp)
+ call salloc (temp, npts, TY_PIXEL)
+
+$if (datatype == r)
+ call gsvector (sx1, xref, yref, xi, npts)
+$else
+ call dgsvector (sx1, xref, yref, xi, npts)
+$endif
+ if (sx2 != NULL) {
+$if (datatype == r)
+ call gsvector (sx2, xref, yref, Mem$t[temp], npts)
+$else
+ call dgsvector (sx2, xref, yref, Mem$t[temp], npts)
+$endif
+ call aadd$t (Mem$t[temp], xi, xi, npts)
+ }
+$if (datatype == r)
+ call gsvector (sy1, xref, yref, eta, npts)
+$else
+ call dgsvector (sy1, xref, yref, eta, npts)
+$endif
+ if (sy2 != NULL) {
+$if (datatype == r)
+ call gsvector (sy2, xref, yref, Mem$t[temp], npts)
+$else
+ call dgsvector (sy2, xref, yref, Mem$t[temp], npts)
+$endif
+
+ call aadd$t (Mem$t[temp], eta, eta, npts)
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_MOUT -- Write the output database file.
+
+procedure geo_mout$t (fit, out, sx1, sy1, sx2, sy2)
+
+pointer fit #I pointer to fitting structure
+int out #I pointer to database file
+pointer sx1, sy1 #I pointer to linear surfaces
+pointer sx2, sy2 #I pointer to distortion surfaces
+
+int i, npts, ncoeff
+pointer sp, str, xcoeff, ycoeff
+PIXEL xrms, yrms, xshift, yshift, xscale, yscale, xrot, yrot
+$if (datatype == r)
+int gsgeti()
+$else
+int dgsgeti()
+$endif
+int rg_wrdstr()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Compute the x and y fit rms.
+ #npts = max (0, GM_NPTS(fit) - GM_NREJECT(fit) - GM_NWTS0(fit))
+ npts = max (0, GM_NPTS(fit) - GM_NWTS0(fit))
+ xrms = max (0.0d0, GM_XRMS(fit))
+ yrms = max (0.0d0, GM_YRMS(fit))
+ if (npts > 1) {
+ xrms = sqrt (xrms / (npts - 1))
+ yrms = sqrt (yrms / (npts - 1))
+ } else {
+ xrms = 0.0d0
+ yrms = 0.0d0
+ }
+
+ # Print title.
+ call dtptime (out)
+ call dtput (out, "begin\t%s\n")
+ call pargstr (GM_RECORD(fit))
+
+ # Print the x and y mean values.
+ call dtput (out, "\txrefmean\t%g\n")
+ call pargd (GM_XOREF(fit))
+ call dtput (out, "\tyrefmean\t%g\n")
+ call pargd (GM_YOREF(fit))
+ call dtput (out, "\txmean\t\t%g\n")
+ call pargd (GM_XOIN(fit))
+ call dtput (out, "\tymean\t\t%g\n")
+ call pargd (GM_YOIN(fit))
+
+ # Print some of the fitting parameters.
+ if (rg_wrdstr (GM_FIT(fit), Memc[str], SZ_FNAME, GM_GEOMETRIES) <= 0)
+ call strcpy ("general", Memc[str], SZ_FNAME)
+ call dtput (out, "\tgeometry\t%s\n")
+ call pargstr (Memc[str])
+ if (rg_wrdstr (GM_FUNCTION(fit), Memc[str], SZ_FNAME, GM_FUNCS) <= 0)
+ call strcpy ("polynomial", Memc[str], SZ_FNAME)
+ call dtput (out, "\tfunction\t%s\n")
+ call pargstr (Memc[str])
+
+ # Output the geometric parameters.
+ call geo_lcoeff$t (sx1, sy1, xshift, yshift, xscale, yscale, xrot, yrot)
+ call dtput (out, "\txshift\t\t%g\n")
+ call parg$t (xshift)
+ call dtput (out, "\tyshift\t\t%g\n")
+ call parg$t (yshift)
+ call dtput (out, "\txmag\t\t%g\n")
+ call parg$t (xscale)
+ call dtput (out, "\tymag\t\t%g\n")
+ call parg$t (yscale)
+ call dtput (out, "\txrotation\t%g\n")
+ call parg$t (xrot)
+ call dtput (out, "\tyrotation\t%g\n")
+ call parg$t (yrot)
+
+ # Out the rms values.
+ call dtput (out, "\txrms\t\t%g\n")
+ call parg$t (PIXEL(xrms))
+ call dtput (out, "\tyrms\t\t%g\n")
+ call parg$t (PIXEL(yrms))
+
+ # Allocate memory for linear coefficients.
+$if (datatype == r)
+ ncoeff = max (gsgeti (sx1, GSNSAVE), gsgeti (sy1, GSNSAVE))
+$else
+ ncoeff = max (dgsgeti (sx1, GSNSAVE), dgsgeti (sy1, GSNSAVE))
+$endif
+ call calloc (xcoeff, ncoeff, TY_PIXEL)
+ call calloc (ycoeff, ncoeff, TY_PIXEL)
+
+ # Output the linear coefficients.
+$if (datatype == r)
+ call gssave (sx1, Mem$t[xcoeff])
+ call gssave (sy1, Mem$t[ycoeff])
+$else
+ call dgssave (sx1, Mem$t[xcoeff])
+ call dgssave (sy1, Mem$t[ycoeff])
+$endif
+ call dtput (out, "\tsurface1\t%d\n")
+ call pargi (ncoeff)
+ do i = 1, ncoeff {
+ call dtput (out, "\t\t\t%g\t%g\n")
+ call parg$t (Mem$t[xcoeff+i-1])
+ call parg$t (Mem$t[ycoeff+i-1])
+ }
+
+ call mfree (xcoeff, TY_PIXEL)
+ call mfree (ycoeff, TY_PIXEL)
+
+ # Allocate memory for higer order coefficients.
+ if (sx2 == NULL)
+ ncoeff = 0
+ else
+$if (datatype == r)
+ ncoeff = gsgeti (sx2, GSNSAVE)
+$else
+ ncoeff = dgsgeti (sx2, GSNSAVE)
+$endif
+ if (sy2 == NULL)
+ ncoeff = max (0, ncoeff)
+ else
+$if (datatype == r)
+ ncoeff = max (gsgeti (sy2, GSNSAVE), ncoeff)
+$else
+ ncoeff = max (dgsgeti (sy2, GSNSAVE), ncoeff)
+$endif
+ call calloc (xcoeff, ncoeff, TY_PIXEL)
+ call calloc (ycoeff, ncoeff, TY_PIXEL)
+
+ # Save the coefficients.
+$if (datatype == r)
+ call gssave (sx2, Mem$t[xcoeff])
+ call gssave (sy2, Mem$t[ycoeff])
+$else
+ call dgssave (sx2, Mem$t[xcoeff])
+ call dgssave (sy2, Mem$t[ycoeff])
+$endif
+
+ # Output the coefficients.
+ call dtput (out, "\tsurface2\t%d\n")
+ call pargi (ncoeff)
+ do i = 1, ncoeff {
+ call dtput (out, "\t\t\t%g\t%g\n")
+ call parg$t (Mem$t[xcoeff+i-1])
+ call parg$t (Mem$t[ycoeff+i-1])
+ }
+
+ # Cleanup.
+ call mfree (xcoeff, TY_PIXEL)
+ call mfree (ycoeff, TY_PIXEL)
+ call sfree (sp)
+end
+
+
+# GEO_PLIST -- Print the input, output, and fitted data and the residuals.
+
+procedure geo_plist$t (fd, fit, xref, yref, xin, yin, xfit, yfit, wts, npts)
+
+int fd #I the results file descriptor
+pointer fit #I pointer to the fit structure
+PIXEL xref[ARB] #I the input x coordinates
+PIXEL yref[ARB] #I the input y coordinates
+PIXEL xin[ARB] #I the input ra / longitude coordinates
+PIXEL yin[ARB] #I the input dec / latitude coordinates
+PIXEL xfit[ARB] #I the fitted ra / longitude coordinates
+PIXEL yfit[ARB] #I the fitted dec / latitude coordinates
+PIXEL wts[ARB] #I the weights array
+int npts #I the number of data points
+
+int i, index
+pointer sp, fmtstr, twts
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (fmtstr, SZ_LINE, TY_CHAR)
+ call salloc (twts, npts, TY_PIXEL)
+
+ # Compute the weights.
+ call amov$t (wts, Mem$t[twts], npts)
+ do i = 1, GM_NREJECT(fit) {
+ index = Memi[GM_REJ(fit)+i-1]
+ if (wts[index] > PIXEL(0.0))
+ Mem$t[twts+index-1] = PIXEL(0.0)
+ }
+
+ # Print banner.
+ call fprintf (fd, "\n# Input Coordinate Listing\n")
+ call fprintf (fd, "# Column 1: X (reference) \n")
+ call fprintf (fd, "# Column 2: Y (reference)\n")
+ call fprintf (fd, "# Column 3: X (input)\n")
+ call fprintf (fd, "# Column 4: Y (input)\n")
+ call fprintf (fd, "# Column 5: X (fit)\n")
+ call fprintf (fd, "# Column 6: Y (fit)\n")
+ call fprintf (fd, "# Column 7: X (residual)\n")
+ call fprintf (fd, "# Column 8: Y (residual)\n\n")
+
+ # Create the format string.
+ call sprintf (Memc[fmtstr], SZ_LINE, "%s %s %s %s %s %s %s %s\n")
+$if (datatype == r)
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+$else
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+$endif
+
+ # Print the data.
+ do i = 1, npts {
+ call fprintf (fd, Memc[fmtstr])
+ call parg$t (xref[i])
+ call parg$t (yref[i])
+ call parg$t (xin[i])
+ call parg$t (yin[i])
+ if (Mem$t[twts+i-1] > 0.0d0) {
+ call parg$t (xfit[i])
+ call parg$t (yfit[i])
+ call parg$t (xin[i] - xfit[i])
+ call parg$t (yin[i] - yfit[i])
+ } else {
+ call parg$t (INDEF)
+ call parg$t (INDEF)
+ call parg$t (INDEF)
+ call parg$t (INDEF)
+ }
+
+ }
+
+ call fprintf (fd, "\n")
+
+ call sfree (sp)
+
+end
+
+# GEO_SHOW -- Print the coordinate mapping parameters.
+
+procedure geo_show$t (fd, fit, sx1, sy1, comment)
+
+int fd #I the output file descriptor
+pointer fit #I pointer to the fit structure
+pointer sx1, sy1 #I pointer to linear surfaces
+int comment #I comment the output ?
+
+PIXEL xshift, yshift, a, b, c, d
+PIXEL xscale, yscale, xrot, yrot
+pointer sp, str
+bool fp_equal$t()
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Compute the geometric parameters.
+ call geo_gcoeff$t (sx1, sy1, xshift, yshift, a, b, c, d)
+
+ if (comment == NO) {
+ call fprintf (fd, "Coordinate mapping parameters\n")
+ } else {
+ call fprintf (fd, "# Coordinate mapping parameters\n")
+ }
+
+ if (comment == NO) {
+ call fprintf (fd,
+ " Mean Xref and Yref: %0.7g %0.7g\n")
+ call pargd (GM_XOREF(fit))
+ call pargd (GM_YOREF(fit))
+ call fprintf (fd,
+ " Mean Xin and Yin: %0.7g %0.7g\n")
+ call pargd (GM_XOIN(fit))
+ call pargd (GM_YOIN(fit))
+ call fprintf (fd,
+ " X and Y shift: %0.7g %0.7g (xin yin)\n")
+ call parg$t (xshift)
+ call parg$t (yshift)
+ } else {
+ call fprintf (fd,
+ "# Mean Xref and Yref: %0.7g %0.7g\n")
+ call pargd (GM_XOREF(fit))
+ call pargd (GM_YOREF(fit))
+ call fprintf (fd,
+ "# Mean Xin and Yin: %0.7g %g0.7\n")
+ call pargd (GM_XOIN(fit))
+ call pargd (GM_YOIN(fit))
+ call fprintf (fd,
+ "# X and Y shift: %0.7g %0.7g (xin yin)\n")
+ call parg$t (xshift)
+ call parg$t (yshift)
+ }
+
+ # Output the scale factors.
+ xscale = sqrt (a * a + c * c)
+ yscale = sqrt (b * b + d * d)
+ if (comment == NO) {
+ call fprintf (fd,
+ " X and Y scale: %0.7g %0.7g (xin / xref yin / yref)\n")
+ call parg$t (xscale)
+ call parg$t (yscale)
+ } else {
+ call fprintf (fd,
+ "# X and Y scale: %0.7g %0.7g (xin / xref yin / yref)\n")
+ call parg$t (xscale)
+ call parg$t (yscale)
+ }
+
+ # Output the rotation factors.
+ if (fp_equal$t (a, PIXEL(0.0)) && fp_equal$t (c, PIXEL(0.0)))
+ xrot = PIXEL(0.0)
+ else
+ xrot = RADTODEG (atan2 (-c, a))
+ if (xrot < PIXEL(0.0))
+ xrot = xrot + PIXEL(360.0)
+ if (fp_equal$t (b, PIXEL(0.0)) && fp_equal$t (d, PIXEL(0.0)))
+ yrot = PIXEL(0.0)
+ else
+ yrot = RADTODEG (atan2 (b, d))
+ if (yrot < PIXEL(0.0))
+ yrot = yrot + PIXEL(360.0)
+ if (comment == NO) {
+ call fprintf (fd,
+ " X and Y axis rotation: %0.5f %0.5f (degrees degrees)\n")
+ call parg$t (xrot)
+ call parg$t (yrot)
+ } else {
+ call fprintf (fd,
+ "# X and Y axis rotation: %0.5f %0.5f (degrees degrees)\n")
+ call parg$t (xrot)
+ call parg$t (yrot)
+ }
+
+ call sfree (sp)
+end
+
+$endfor
diff --git a/pkg/images/immatch/src/geometry/t_geomap.x b/pkg/images/immatch/src/geometry/t_geomap.x
new file mode 100644
index 00000000..6f1c20f0
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/t_geomap.x
@@ -0,0 +1,1509 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <fset.h>
+include <error.h>
+include <mach.h>
+include <math.h>
+include <math/gsurfit.h>
+include "../../../lib/geomap.h"
+
+define GM_REAL 1 # computation type is real
+define GM_DOUBLE 2 # computation type is double
+
+
+
+# T_GEOMAP -- Procedure to calculate the transformation required to transform
+# the coordinate system of a reference image to the coordinate system of
+# an input image. The transformation is of the following form.
+#
+# xin = f (xref, yref)
+# yin = g (xref, yref)
+
+procedure t_geomap ()
+
+bool verbose, interactive
+double xmin, xmax, ymin, ymax, reject
+int geometry, function, calctype, nfiles, list, in, reclist, nrecords
+int xxorder, xyorder, xxterms, yxorder, yyorder, yxterms, maxiter
+int reslist, nresfiles, res
+pointer sp, in_name, str, out, fit, gd, graphics
+real rxmin, rxmax, rymin, rymax
+
+bool clgetb()
+double clgetd()
+int clgeti(), clgwrd(), clplen(), errget(), imtopenp(), imtlen()
+int imtgetim()
+pointer clpopnu(), clgfil(), dtmap(), gopen(), open()
+
+errchk geo_mapr(), geo_mapd()
+
+begin
+ # Get working space.
+ call smark (sp)
+ call salloc (in_name, SZ_FNAME, TY_CHAR)
+ call salloc (graphics, SZ_FNAME, TY_CHAR)
+ call salloc (str, max(SZ_LINE, SZ_FNAME), TY_CHAR)
+
+ # Get input data file(s).
+ list = clpopnu ("input")
+ nfiles = clplen (list)
+
+ # Open database output file.
+ call clgstr ("database", Memc[str], SZ_FNAME)
+ out = dtmap (Memc[str], APPEND)
+
+ # Get minimum and maximum reference values.
+ xmin = clgetd ("xmin")
+ if (IS_INDEFD(xmin))
+ rxmin = INDEFR
+ else
+ rxmin = xmin
+ xmax = clgetd ("xmax")
+ if (IS_INDEFD(xmax))
+ rxmax = INDEFR
+ else
+ rxmax = xmax
+ ymin = clgetd ("ymin")
+ if (IS_INDEFD(ymin))
+ rymin = INDEFR
+ else
+ rymin = ymin
+ ymax = clgetd ("ymax")
+ if (IS_INDEFD(ymax))
+ rymax = INDEFR
+ else
+ rymax = ymax
+
+ # Get the records list.
+ reclist = imtopenp ("transforms")
+ nrecords = imtlen (reclist)
+ if ((nrecords > 0) && (nrecords != nfiles)) {
+ call eprintf (
+ "The number of records is not equal to the number of input files")
+ call clpcls (list)
+ call dtunmap (out)
+ call imtclose (reclist)
+ call sfree (sp)
+ return
+ }
+
+ # Get the results file list.
+ reslist = clpopnu ("results")
+ nresfiles = clplen (reslist)
+ if (nresfiles > 1 && nresfiles != nfiles) {
+ call eprintf ("Error: there are too few results files\n")
+ call clpcls (list)
+ call dtunmap (out)
+ call imtclose (reclist)
+ call clpcls (reslist)
+ call sfree (sp)
+ return
+ }
+
+ # Get the surface fitting parameters.
+ geometry = clgwrd ("fitgeometry", Memc[str], SZ_LINE, GM_GEOMETRIES)
+ function = clgwrd ("function", Memc[str], SZ_LINE, GM_FUNCS)
+ xxorder = clgeti ("xxorder")
+ xyorder = clgeti ("xyorder")
+ xxterms = clgwrd ("xxterms", Memc[str], SZ_LINE, GM_XFUNCS) - 1
+ yxorder = clgeti ("yxorder")
+ yyorder = clgeti ("yyorder")
+ yxterms = clgwrd ("yxterms", Memc[str], SZ_LINE, GM_XFUNCS) - 1
+ maxiter = clgeti ("maxiter")
+ reject = clgetd ("reject")
+ calctype = clgwrd ("calctype", Memc[str], SZ_LINE, ",real,double,")
+
+ # Get the graphics parameters.
+ verbose = clgetb ("verbose")
+ interactive = clgetb ("interactive")
+ call clgstr ("graphics", Memc[graphics], SZ_FNAME)
+
+ # Flush standard output on newline.
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Initialize the fit structure.
+ call geo_minit (fit, GM_NONE, geometry, function, xxorder, xyorder,
+ xxterms, yxorder, yyorder, yxterms, maxiter, reject)
+
+ # Loop over the files.
+ while (clgfil (list, Memc[in_name], SZ_FNAME) != EOF) {
+
+ # Open text file of coordinates.
+ in = open (Memc[in_name], READ_ONLY, TEXT_FILE)
+
+ # Open the results files.
+ if (nresfiles <= 0)
+ res = NULL
+ else if (clgfil (reslist, Memc[str], SZ_FNAME) != EOF)
+ res = open (Memc[str], NEW_FILE, TEXT_FILE)
+
+ # Set file name in structure.
+ if (nrecords > 0) {
+ if (imtgetim (reclist, GM_RECORD(fit), SZ_FNAME) != EOF)
+ ;
+ } else
+ call strcpy (Memc[in_name], GM_RECORD(fit), SZ_FNAME)
+
+ if (verbose && res != STDOUT) {
+ call fstats (in, F_FILENAME, Memc[str], SZ_FNAME)
+ call printf ("\nCoordinate list: %s Transform: %s\n")
+ call pargstr (Memc[str])
+ call pargstr (GM_RECORD(fit))
+ if (res != NULL)
+ call fstats (res, F_FILENAME, Memc[str], SZ_FNAME)
+ else
+ call strcpy ("", Memc[str], SZ_FNAME)
+ call printf (" Results file: %s\n")
+ call pargstr (Memc[str])
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fstats (in, F_FILENAME, Memc[str], SZ_FNAME)
+ call fprintf (res, "\n# Coordinate list: %s Transform: %s\n")
+ call pargstr (Memc[str])
+ call pargstr (GM_RECORD(fit))
+ if (res != NULL)
+ call fstats (res, F_FILENAME, Memc[str], SZ_FNAME)
+ else
+ call strcpy ("", Memc[str], SZ_FNAME)
+ call fprintf (res, "# Results file: %s\n")
+ call pargstr (Memc[str])
+ call flush (STDOUT)
+ }
+
+ if (interactive) {
+ gd = gopen (Memc[graphics], NEW_FILE, STDGRAPH)
+ } else
+ gd = NULL
+
+ iferr {
+ if (calctype == GM_REAL)
+ call geo_mapr (gd, in, out, res, fit, rxmin, rxmax, rymin,
+ rymax, verbose)
+ else
+ call geo_mapd (gd, in, out, res, fit, xmin, xmax, ymin,
+ ymax, verbose)
+ } then {
+ if (verbose && res != STDOUT) {
+ call printf ("Error fitting coordinate list: %s\n")
+ call pargstr (Memc[in_name])
+ call flush (STDOUT)
+ if (errget (Memc[str], SZ_LINE) == 0)
+ ;
+ call printf ("\t%s\n")
+ call pargstr (Memc[str))
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Error fitting coordinate list: %s\n")
+ call pargstr (Memc[in_name])
+ call flush (STDOUT)
+ if (errget (Memc[str], SZ_LINE) == 0)
+ ;
+ call fprintf (res, "# %s\n")
+ call pargstr (Memc[str))
+ }
+ }
+
+ call close (in)
+ if (nresfiles == nfiles)
+ call close ( res)
+
+ if (gd != NULL)
+ call gclose (gd)
+ }
+
+ # Close up.
+ call geo_free (fit)
+ if (nresfiles < nfiles)
+ call close ( res)
+ call dtunmap (out)
+ call imtclose (reclist)
+ call clpcls (list)
+ call sfree (sp)
+end
+
+
+
+
+
+# GEO_MAP -- Procedure to calculate the coordinate transformations
+
+procedure geo_mapr (gd, in, out, res, fit, xmin, xmax, ymin, ymax, verbose)
+
+pointer gd #I the graphics stream
+int in #I the input file descriptor
+pointer out #I the output file descriptor
+int res #I the results file descriptor
+pointer fit #I pointer to fit parameters
+real xmin, xmax #I max and min xref values
+real ymin, ymax #I max and min yref values
+bool verbose #I verbose mode
+
+int npts, ngood
+pointer sp, str, xref, yref, xin, yin, wts, xfit, yfit, xerrmsg, yerrmsg
+pointer sx1, sy1, sx2, sy2
+real mintemp, maxtemp
+
+real asumr()
+int geo_rdxyr()
+errchk geo_fitr, geo_mgfitr()
+
+begin
+ # Get working space.
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+ call salloc (xerrmsg, SZ_LINE, TY_CHAR)
+ call salloc (yerrmsg, SZ_LINE, TY_CHAR)
+
+ # Initialize pointers.
+ xref = NULL
+ yref = NULL
+ xin = NULL
+ yin = NULL
+ wts = NULL
+
+ # Read in data and check that data is in range.
+ npts = geo_rdxyr (in, xref, yref, xin, yin, xmin, xmax, ymin, ymax)
+ if (npts <= 0) {
+ call fstats (in, F_FILENAME, Memc[str], SZ_FNAME)
+ call printf ("Coordinate list: %s has no data in range.\n")
+ call pargstr (Memc[str])
+ call sfree (sp)
+ return
+ }
+
+ # Compute the mean of the reference and input coordinates.
+ GM_XOREF(fit) = double (asumr (Memr[xref], npts) / npts)
+ GM_YOREF(fit) = double (asumr (Memr[yref], npts) / npts)
+ GM_XOIN(fit) = double (asumr (Memr[xin], npts) / npts)
+ GM_YOIN(fit) = double (asumr (Memr[yin], npts) / npts)
+
+ # Set the reference point for the projections to INDEF.
+ GM_XREFPT(fit) = INDEFD
+ GM_YREFPT(fit) = INDEFD
+
+ # Compute the weights.
+ call malloc (xfit, npts, TY_REAL)
+ call malloc (yfit, npts, TY_REAL)
+ call malloc (wts, npts, TY_REAL)
+ call amovkr (real(1.), Memr[wts], npts)
+
+ # Determine the x max and min.
+ if (IS_INDEFR(xmin) || IS_INDEFR(xmax)) {
+ call alimr (Memr[xref], npts, mintemp, maxtemp)
+ if (! IS_INDEFR(xmin))
+ GM_XMIN(fit) = double (xmin)
+ else
+ GM_XMIN(fit) = double (mintemp)
+ if (! IS_INDEFR(xmax))
+ GM_XMAX(fit) = double (xmax)
+ else
+ GM_XMAX(fit) = double (maxtemp)
+ } else {
+ GM_XMIN(fit) = double (xmin)
+ GM_XMAX(fit) = double (xmax)
+ }
+
+ # Determine the y max and min.
+ if (IS_INDEFR(ymin) || IS_INDEFR(ymax)) {
+ call alimr (Memr[yref], npts, mintemp, maxtemp)
+ if (! IS_INDEFR(ymin))
+ GM_YMIN(fit) = double (ymin)
+ else
+ GM_YMIN(fit) = double (mintemp)
+ if (! IS_INDEFR(ymax))
+ GM_YMAX(fit) = double (ymax)
+ else
+ GM_YMAX(fit) = double (maxtemp)
+ } else {
+ GM_YMIN(fit) = double (ymin)
+ GM_YMAX(fit) = double (ymax)
+ }
+
+ # Initalize surface pointers.
+ sx1 = NULL
+ sy1 = NULL
+ sx2 = NULL
+ sy2 = NULL
+
+ # Fit the data.
+ if (gd != NULL) {
+ iferr {
+ call geo_mgfitr (gd, fit, sx1, sy1, sx2, sy2, Memr[xref],
+ Memr[yref], Memr[xin], Memr[yin], Memr[wts], npts,
+ Memc[xerrmsg], Memc[yerrmsg], SZ_LINE)
+ } then {
+ call gdeactivate (gd, 0)
+ call mfree (xfit, TY_REAL)
+ call mfree (yfit, TY_REAL)
+ call mfree (wts, TY_REAL)
+ call geo_mmfreer (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+ call error (3, "Too few points for X or Y fits.")
+ }
+ call gdeactivate (gd, 0)
+ if (verbose && res != STDOUT) {
+ call printf ("Coordinate mapping status\n")
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Coordinate mapping status\n")
+ }
+ } else {
+ if (verbose && res != STDOUT) {
+ call printf ("Coordinate mapping status\n ")
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Coordinate mapping status\n# ")
+ }
+ iferr {
+ call geo_fitr (fit, sx1, sy1, sx2, sy2, Memr[xref],
+ Memr[yref], Memr[xin], Memr[yin], Memr[wts], npts,
+ Memc[xerrmsg], Memc[yerrmsg], SZ_LINE)
+ } then {
+ call mfree (xfit, TY_REAL)
+ call mfree (yfit, TY_REAL)
+ call mfree (wts, TY_REAL)
+ call geo_mmfreer (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+ call error (3, "Too few points for X or Y fits.")
+ }
+ if (verbose && res != STDOUT) {
+ call printf ("%s %s\n")
+ call pargstr (Memc[xerrmsg])
+ call pargstr (Memc[yerrmsg])
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "%s %s\n")
+ call pargstr (Memc[xerrmsg])
+ call pargstr (Memc[yerrmsg])
+ call flush (STDOUT)
+ }
+ }
+ ngood = GM_NPTS(fit) - GM_NWTS0(fit)
+ if (verbose && res != STDOUT) {
+ call printf (" Xin and Yin fit rms: %0.7g %0.7g\n")
+ if (ngood <= 1) {
+ call pargd (0.0d0)
+ call pargd (0.0d0)
+ } else {
+ call pargd (sqrt (GM_XRMS(fit) / (ngood - 1)))
+ call pargd (sqrt (GM_YRMS(fit) / (ngood - 1)))
+ }
+ call geo_showr (STDOUT, fit, sx1, sy1, NO)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Xin and Yin fit rms: %0.7g %0.7g\n")
+ if (ngood <= 1) {
+ call pargd (0.0)
+ call pargd (0.0)
+ } else {
+ call pargd (sqrt (GM_XRMS(fit) / (ngood - 1)))
+ call pargd (sqrt (GM_YRMS(fit) / (ngood - 1)))
+ }
+ call geo_showr (res, fit, sx1, sy1, YES)
+ }
+
+ # Compute and print the fitted x and y values.
+ if (res != NULL) {
+ call geo_evalr (sx1, sy1, sx2, sy2, Memr[xref], Memr[yref],
+ Memr[xfit], Memr[yfit], npts)
+ call geo_plistr (res, fit, Memr[xref], Memr[yref], Memr[xin],
+ Memr[yin], Memr[xfit], Memr[yfit], Memr[wts], npts)
+ }
+
+ # Free the data
+ if (xref != NULL)
+ call mfree (xref, TY_REAL)
+ if (yref != NULL)
+ call mfree (yref, TY_REAL)
+ if (xin != NULL)
+ call mfree (xin, TY_REAL)
+ if (yin != NULL)
+ call mfree (yin, TY_REAL)
+ if (xfit != NULL)
+ call mfree (xfit, TY_REAL)
+ if (yfit != NULL)
+ call mfree (yfit, TY_REAL)
+ if (wts != NULL)
+ call mfree (wts, TY_REAL)
+
+ # Output the data.
+ call geo_moutr (fit, out, sx1, sy1, sx2, sy2)
+
+ # Free the space and close files.
+ call geo_mmfreer (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+end
+
+
+define GEO_DEFBUFSIZE 1000 # default data buffer sizes
+
+# GEO_RDXY -- Read in the data points.
+
+int procedure geo_rdxyr (fd, xref, yref, xin, yin, xmin, xmax, ymin, ymax)
+
+int fd # the input file descriptor
+pointer xref # the x reference coordinates
+pointer yref # the y reference coordinates
+pointer xin # the x coordinates
+pointer yin # the y coordinates
+real xmin, xmax # the range of the x coordinates
+real ymin, ymax # the range of the y coordinates
+
+int npts, bufsize
+int fscan(), nscan()
+
+begin
+ bufsize = GEO_DEFBUFSIZE
+ call malloc (xref, bufsize, TY_REAL)
+ call malloc (yref, bufsize, TY_REAL)
+ call malloc (xin, bufsize, TY_REAL)
+ call malloc (yin, bufsize, TY_REAL)
+
+ npts = 0
+ while (fscan (fd) != EOF) {
+
+ # Decode the data.
+ call gargr (Memr[xref+npts])
+ call gargr (Memr[yref+npts])
+ call gargr (Memr[xin+npts])
+ call gargr (Memr[yin+npts])
+ if (nscan() < 4)
+ next
+
+ # Check the data limits.
+ if (! IS_INDEFR(xmin)) {
+ if (Memr[xref+npts] < xmin)
+ next
+ }
+ if (! IS_INDEFR(xmax)) {
+ if (Memr[xref+npts] > xmax)
+ next
+ }
+ if (! IS_INDEFR(ymin)) {
+ if (Memr[yref+npts] < ymin)
+ next
+ }
+ if (! IS_INDEFR(ymax)) {
+ if (Memr[yref+npts] > ymax)
+ next
+ }
+
+ npts = npts + 1
+ if (npts >= bufsize) {
+ bufsize = bufsize + GEO_DEFBUFSIZE
+ call realloc (xref, bufsize, TY_REAL)
+ call realloc (yref, bufsize, TY_REAL)
+ call realloc (xin, bufsize, TY_REAL)
+ call realloc (yin, bufsize, TY_REAL)
+ }
+ }
+
+ if (npts <= 0) {
+ call mfree (xref, TY_REAL)
+ call mfree (yref, TY_REAL)
+ call mfree (xin, TY_REAL)
+ call mfree (yin, TY_REAL)
+ xref = NULL
+ yref = NULL
+ xin = NULL
+ yin = NULL
+ } else if (npts < bufsize) {
+ call realloc (xref, npts, TY_REAL)
+ call realloc (yref, npts, TY_REAL)
+ call realloc (xin, npts, TY_REAL)
+ call realloc (yin, npts, TY_REAL)
+ }
+
+ return (npts)
+end
+
+
+# GEO_EVAL -- Evalute the fit.
+
+procedure geo_evalr (sx1, sy1, sx2, sy2, xref, yref, xi, eta, npts)
+
+pointer sx1, sy1 #I pointer to linear surfaces
+pointer sx2, sy2 #I pointer to higher order surfaces
+real xref[ARB] #I the x reference coordinates
+real yref[ARB] #I the y reference coordinates
+real xi[ARB] #O the fitted xi coordinates
+real eta[ARB] #O the fitted eta coordinates
+int npts #I the number of points
+
+pointer sp, temp
+
+begin
+ call smark (sp)
+ call salloc (temp, npts, TY_REAL)
+
+ call gsvector (sx1, xref, yref, xi, npts)
+ if (sx2 != NULL) {
+ call gsvector (sx2, xref, yref, Memr[temp], npts)
+ call aaddr (Memr[temp], xi, xi, npts)
+ }
+ call gsvector (sy1, xref, yref, eta, npts)
+ if (sy2 != NULL) {
+ call gsvector (sy2, xref, yref, Memr[temp], npts)
+
+ call aaddr (Memr[temp], eta, eta, npts)
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_MOUT -- Write the output database file.
+
+procedure geo_moutr (fit, out, sx1, sy1, sx2, sy2)
+
+pointer fit #I pointer to fitting structure
+int out #I pointer to database file
+pointer sx1, sy1 #I pointer to linear surfaces
+pointer sx2, sy2 #I pointer to distortion surfaces
+
+int i, npts, ncoeff
+pointer sp, str, xcoeff, ycoeff
+real xrms, yrms, xshift, yshift, xscale, yscale, xrot, yrot
+int gsgeti()
+int rg_wrdstr()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Compute the x and y fit rms.
+ #npts = max (0, GM_NPTS(fit) - GM_NREJECT(fit) - GM_NWTS0(fit))
+ npts = max (0, GM_NPTS(fit) - GM_NWTS0(fit))
+ xrms = max (0.0d0, GM_XRMS(fit))
+ yrms = max (0.0d0, GM_YRMS(fit))
+ if (npts > 1) {
+ xrms = sqrt (xrms / (npts - 1))
+ yrms = sqrt (yrms / (npts - 1))
+ } else {
+ xrms = 0.0d0
+ yrms = 0.0d0
+ }
+
+ # Print title.
+ call dtptime (out)
+ call dtput (out, "begin\t%s\n")
+ call pargstr (GM_RECORD(fit))
+
+ # Print the x and y mean values.
+ call dtput (out, "\txrefmean\t%g\n")
+ call pargd (GM_XOREF(fit))
+ call dtput (out, "\tyrefmean\t%g\n")
+ call pargd (GM_YOREF(fit))
+ call dtput (out, "\txmean\t\t%g\n")
+ call pargd (GM_XOIN(fit))
+ call dtput (out, "\tymean\t\t%g\n")
+ call pargd (GM_YOIN(fit))
+
+ # Print some of the fitting parameters.
+ if (rg_wrdstr (GM_FIT(fit), Memc[str], SZ_FNAME, GM_GEOMETRIES) <= 0)
+ call strcpy ("general", Memc[str], SZ_FNAME)
+ call dtput (out, "\tgeometry\t%s\n")
+ call pargstr (Memc[str])
+ if (rg_wrdstr (GM_FUNCTION(fit), Memc[str], SZ_FNAME, GM_FUNCS) <= 0)
+ call strcpy ("polynomial", Memc[str], SZ_FNAME)
+ call dtput (out, "\tfunction\t%s\n")
+ call pargstr (Memc[str])
+
+ # Output the geometric parameters.
+ call geo_lcoeffr (sx1, sy1, xshift, yshift, xscale, yscale, xrot, yrot)
+ call dtput (out, "\txshift\t\t%g\n")
+ call pargr (xshift)
+ call dtput (out, "\tyshift\t\t%g\n")
+ call pargr (yshift)
+ call dtput (out, "\txmag\t\t%g\n")
+ call pargr (xscale)
+ call dtput (out, "\tymag\t\t%g\n")
+ call pargr (yscale)
+ call dtput (out, "\txrotation\t%g\n")
+ call pargr (xrot)
+ call dtput (out, "\tyrotation\t%g\n")
+ call pargr (yrot)
+
+ # Out the rms values.
+ call dtput (out, "\txrms\t\t%g\n")
+ call pargr (real(xrms))
+ call dtput (out, "\tyrms\t\t%g\n")
+ call pargr (real(yrms))
+
+ # Allocate memory for linear coefficients.
+ ncoeff = max (gsgeti (sx1, GSNSAVE), gsgeti (sy1, GSNSAVE))
+ call calloc (xcoeff, ncoeff, TY_REAL)
+ call calloc (ycoeff, ncoeff, TY_REAL)
+
+ # Output the linear coefficients.
+ call gssave (sx1, Memr[xcoeff])
+ call gssave (sy1, Memr[ycoeff])
+ call dtput (out, "\tsurface1\t%d\n")
+ call pargi (ncoeff)
+ do i = 1, ncoeff {
+ call dtput (out, "\t\t\t%g\t%g\n")
+ call pargr (Memr[xcoeff+i-1])
+ call pargr (Memr[ycoeff+i-1])
+ }
+
+ call mfree (xcoeff, TY_REAL)
+ call mfree (ycoeff, TY_REAL)
+
+ # Allocate memory for higer order coefficients.
+ if (sx2 == NULL)
+ ncoeff = 0
+ else
+ ncoeff = gsgeti (sx2, GSNSAVE)
+ if (sy2 == NULL)
+ ncoeff = max (0, ncoeff)
+ else
+ ncoeff = max (gsgeti (sy2, GSNSAVE), ncoeff)
+ call calloc (xcoeff, ncoeff, TY_REAL)
+ call calloc (ycoeff, ncoeff, TY_REAL)
+
+ # Save the coefficients.
+ call gssave (sx2, Memr[xcoeff])
+ call gssave (sy2, Memr[ycoeff])
+
+ # Output the coefficients.
+ call dtput (out, "\tsurface2\t%d\n")
+ call pargi (ncoeff)
+ do i = 1, ncoeff {
+ call dtput (out, "\t\t\t%g\t%g\n")
+ call pargr (Memr[xcoeff+i-1])
+ call pargr (Memr[ycoeff+i-1])
+ }
+
+ # Cleanup.
+ call mfree (xcoeff, TY_REAL)
+ call mfree (ycoeff, TY_REAL)
+ call sfree (sp)
+end
+
+
+# GEO_PLIST -- Print the input, output, and fitted data and the residuals.
+
+procedure geo_plistr (fd, fit, xref, yref, xin, yin, xfit, yfit, wts, npts)
+
+int fd #I the results file descriptor
+pointer fit #I pointer to the fit structure
+real xref[ARB] #I the input x coordinates
+real yref[ARB] #I the input y coordinates
+real xin[ARB] #I the input ra / longitude coordinates
+real yin[ARB] #I the input dec / latitude coordinates
+real xfit[ARB] #I the fitted ra / longitude coordinates
+real yfit[ARB] #I the fitted dec / latitude coordinates
+real wts[ARB] #I the weights array
+int npts #I the number of data points
+
+int i, index
+pointer sp, fmtstr, twts
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (fmtstr, SZ_LINE, TY_CHAR)
+ call salloc (twts, npts, TY_REAL)
+
+ # Compute the weights.
+ call amovr (wts, Memr[twts], npts)
+ do i = 1, GM_NREJECT(fit) {
+ index = Memi[GM_REJ(fit)+i-1]
+ if (wts[index] > real(0.0))
+ Memr[twts+index-1] = real(0.0)
+ }
+
+ # Print banner.
+ call fprintf (fd, "\n# Input Coordinate Listing\n")
+ call fprintf (fd, "# Column 1: X (reference) \n")
+ call fprintf (fd, "# Column 2: Y (reference)\n")
+ call fprintf (fd, "# Column 3: X (input)\n")
+ call fprintf (fd, "# Column 4: Y (input)\n")
+ call fprintf (fd, "# Column 5: X (fit)\n")
+ call fprintf (fd, "# Column 6: Y (fit)\n")
+ call fprintf (fd, "# Column 7: X (residual)\n")
+ call fprintf (fd, "# Column 8: Y (residual)\n\n")
+
+ # Create the format string.
+ call sprintf (Memc[fmtstr], SZ_LINE, "%s %s %s %s %s %s %s %s\n")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+ call pargstr ("%9.7g")
+
+ # Print the data.
+ do i = 1, npts {
+ call fprintf (fd, Memc[fmtstr])
+ call pargr (xref[i])
+ call pargr (yref[i])
+ call pargr (xin[i])
+ call pargr (yin[i])
+ if (Memr[twts+i-1] > 0.0d0) {
+ call pargr (xfit[i])
+ call pargr (yfit[i])
+ call pargr (xin[i] - xfit[i])
+ call pargr (yin[i] - yfit[i])
+ } else {
+ call pargr (INDEFR)
+ call pargr (INDEFR)
+ call pargr (INDEFR)
+ call pargr (INDEFR)
+ }
+
+ }
+
+ call fprintf (fd, "\n")
+
+ call sfree (sp)
+
+end
+
+# GEO_SHOW -- Print the coordinate mapping parameters.
+
+procedure geo_showr (fd, fit, sx1, sy1, comment)
+
+int fd #I the output file descriptor
+pointer fit #I pointer to the fit structure
+pointer sx1, sy1 #I pointer to linear surfaces
+int comment #I comment the output ?
+
+real xshift, yshift, a, b, c, d
+real xscale, yscale, xrot, yrot
+pointer sp, str
+bool fp_equalr()
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Compute the geometric parameters.
+ call geo_gcoeffr (sx1, sy1, xshift, yshift, a, b, c, d)
+
+ if (comment == NO) {
+ call fprintf (fd, "Coordinate mapping parameters\n")
+ } else {
+ call fprintf (fd, "# Coordinate mapping parameters\n")
+ }
+
+ if (comment == NO) {
+ call fprintf (fd,
+ " Mean Xref and Yref: %0.7g %0.7g\n")
+ call pargd (GM_XOREF(fit))
+ call pargd (GM_YOREF(fit))
+ call fprintf (fd,
+ " Mean Xin and Yin: %0.7g %0.7g\n")
+ call pargd (GM_XOIN(fit))
+ call pargd (GM_YOIN(fit))
+ call fprintf (fd,
+ " X and Y shift: %0.7g %0.7g (xin yin)\n")
+ call pargr (xshift)
+ call pargr (yshift)
+ } else {
+ call fprintf (fd,
+ "# Mean Xref and Yref: %0.7g %0.7g\n")
+ call pargd (GM_XOREF(fit))
+ call pargd (GM_YOREF(fit))
+ call fprintf (fd,
+ "# Mean Xin and Yin: %0.7g %g0.7\n")
+ call pargd (GM_XOIN(fit))
+ call pargd (GM_YOIN(fit))
+ call fprintf (fd,
+ "# X and Y shift: %0.7g %0.7g (xin yin)\n")
+ call pargr (xshift)
+ call pargr (yshift)
+ }
+
+ # Output the scale factors.
+ xscale = sqrt (a * a + c * c)
+ yscale = sqrt (b * b + d * d)
+ if (comment == NO) {
+ call fprintf (fd,
+ " X and Y scale: %0.7g %0.7g (xin / xref yin / yref)\n")
+ call pargr (xscale)
+ call pargr (yscale)
+ } else {
+ call fprintf (fd,
+ "# X and Y scale: %0.7g %0.7g (xin / xref yin / yref)\n")
+ call pargr (xscale)
+ call pargr (yscale)
+ }
+
+ # Output the rotation factors.
+ if (fp_equalr (a, real(0.0)) && fp_equalr (c, real(0.0)))
+ xrot = real(0.0)
+ else
+ xrot = RADTODEG (atan2 (-c, a))
+ if (xrot < real(0.0))
+ xrot = xrot + real(360.0)
+ if (fp_equalr (b, real(0.0)) && fp_equalr (d, real(0.0)))
+ yrot = real(0.0)
+ else
+ yrot = RADTODEG (atan2 (b, d))
+ if (yrot < real(0.0))
+ yrot = yrot + real(360.0)
+ if (comment == NO) {
+ call fprintf (fd,
+ " X and Y axis rotation: %0.5f %0.5f (degrees degrees)\n")
+ call pargr (xrot)
+ call pargr (yrot)
+ } else {
+ call fprintf (fd,
+ "# X and Y axis rotation: %0.5f %0.5f (degrees degrees)\n")
+ call pargr (xrot)
+ call pargr (yrot)
+ }
+
+ call sfree (sp)
+end
+
+
+
+# GEO_MAP -- Procedure to calculate the coordinate transformations
+
+procedure geo_mapd (gd, in, out, res, fit, xmin, xmax, ymin, ymax, verbose)
+
+pointer gd #I the graphics stream
+int in #I the input file descriptor
+pointer out #I the output file descriptor
+int res #I the results file descriptor
+pointer fit #I pointer to fit parameters
+double xmin, xmax #I max and min xref values
+double ymin, ymax #I max and min yref values
+bool verbose #I verbose mode
+
+int npts, ngood
+pointer sp, str, xref, yref, xin, yin, wts, xfit, yfit, xerrmsg, yerrmsg
+pointer sx1, sy1, sx2, sy2
+double mintemp, maxtemp
+
+double asumd()
+int geo_rdxyd()
+errchk geo_fitd, geo_mgfitd()
+
+begin
+ # Get working space.
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+ call salloc (xerrmsg, SZ_LINE, TY_CHAR)
+ call salloc (yerrmsg, SZ_LINE, TY_CHAR)
+
+ # Initialize pointers.
+ xref = NULL
+ yref = NULL
+ xin = NULL
+ yin = NULL
+ wts = NULL
+
+ # Read in data and check that data is in range.
+ npts = geo_rdxyd (in, xref, yref, xin, yin, xmin, xmax, ymin, ymax)
+ if (npts <= 0) {
+ call fstats (in, F_FILENAME, Memc[str], SZ_FNAME)
+ call printf ("Coordinate list: %s has no data in range.\n")
+ call pargstr (Memc[str])
+ call sfree (sp)
+ return
+ }
+
+ # Compute the mean of the reference and input coordinates.
+ GM_XOREF(fit) = double (asumd (Memd[xref], npts) / npts)
+ GM_YOREF(fit) = double (asumd (Memd[yref], npts) / npts)
+ GM_XOIN(fit) = double (asumd (Memd[xin], npts) / npts)
+ GM_YOIN(fit) = double (asumd (Memd[yin], npts) / npts)
+
+ # Set the reference point for the projections to INDEF.
+ GM_XREFPT(fit) = INDEFD
+ GM_YREFPT(fit) = INDEFD
+
+ # Compute the weights.
+ call malloc (xfit, npts, TY_DOUBLE)
+ call malloc (yfit, npts, TY_DOUBLE)
+ call malloc (wts, npts, TY_DOUBLE)
+ call amovkd (double(1.), Memd[wts], npts)
+
+ # Determine the x max and min.
+ if (IS_INDEFD(xmin) || IS_INDEFD(xmax)) {
+ call alimd (Memd[xref], npts, mintemp, maxtemp)
+ if (! IS_INDEFD(xmin))
+ GM_XMIN(fit) = double (xmin)
+ else
+ GM_XMIN(fit) = double (mintemp)
+ if (! IS_INDEFD(xmax))
+ GM_XMAX(fit) = double (xmax)
+ else
+ GM_XMAX(fit) = double (maxtemp)
+ } else {
+ GM_XMIN(fit) = double (xmin)
+ GM_XMAX(fit) = double (xmax)
+ }
+
+ # Determine the y max and min.
+ if (IS_INDEFD(ymin) || IS_INDEFD(ymax)) {
+ call alimd (Memd[yref], npts, mintemp, maxtemp)
+ if (! IS_INDEFD(ymin))
+ GM_YMIN(fit) = double (ymin)
+ else
+ GM_YMIN(fit) = double (mintemp)
+ if (! IS_INDEFD(ymax))
+ GM_YMAX(fit) = double (ymax)
+ else
+ GM_YMAX(fit) = double (maxtemp)
+ } else {
+ GM_YMIN(fit) = double (ymin)
+ GM_YMAX(fit) = double (ymax)
+ }
+
+ # Initalize surface pointers.
+ sx1 = NULL
+ sy1 = NULL
+ sx2 = NULL
+ sy2 = NULL
+
+ # Fit the data.
+ if (gd != NULL) {
+ iferr {
+ call geo_mgfitd (gd, fit, sx1, sy1, sx2, sy2, Memd[xref],
+ Memd[yref], Memd[xin], Memd[yin], Memd[wts], npts,
+ Memc[xerrmsg], Memc[yerrmsg], SZ_LINE)
+ } then {
+ call gdeactivate (gd, 0)
+ call mfree (xfit, TY_DOUBLE)
+ call mfree (yfit, TY_DOUBLE)
+ call mfree (wts, TY_DOUBLE)
+ call geo_mmfreed (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+ call error (3, "Too few points for X or Y fits.")
+ }
+ call gdeactivate (gd, 0)
+ if (verbose && res != STDOUT) {
+ call printf ("Coordinate mapping status\n")
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Coordinate mapping status\n")
+ }
+ } else {
+ if (verbose && res != STDOUT) {
+ call printf ("Coordinate mapping status\n ")
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Coordinate mapping status\n# ")
+ }
+ iferr {
+ call geo_fitd (fit, sx1, sy1, sx2, sy2, Memd[xref],
+ Memd[yref], Memd[xin], Memd[yin], Memd[wts], npts,
+ Memc[xerrmsg], Memc[yerrmsg], SZ_LINE)
+ } then {
+ call mfree (xfit, TY_DOUBLE)
+ call mfree (yfit, TY_DOUBLE)
+ call mfree (wts, TY_DOUBLE)
+ call geo_mmfreed (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+ call error (3, "Too few points for X or Y fits.")
+ }
+ if (verbose && res != STDOUT) {
+ call printf ("%s %s\n")
+ call pargstr (Memc[xerrmsg])
+ call pargstr (Memc[yerrmsg])
+ call flush (STDOUT)
+ }
+ if (res != NULL) {
+ call fprintf (res, "%s %s\n")
+ call pargstr (Memc[xerrmsg])
+ call pargstr (Memc[yerrmsg])
+ call flush (STDOUT)
+ }
+ }
+ ngood = GM_NPTS(fit) - GM_NWTS0(fit)
+ if (verbose && res != STDOUT) {
+ call printf (" Xin and Yin fit rms: %0.7g %0.7g\n")
+ if (ngood <= 1) {
+ call pargd (0.0d0)
+ call pargd (0.0d0)
+ } else {
+ call pargd (sqrt (GM_XRMS(fit) / (ngood - 1)))
+ call pargd (sqrt (GM_YRMS(fit) / (ngood - 1)))
+ }
+ call geo_showd (STDOUT, fit, sx1, sy1, NO)
+ }
+ if (res != NULL) {
+ call fprintf (res, "# Xin and Yin fit rms: %0.7g %0.7g\n")
+ if (ngood <= 1) {
+ call pargd (0.0)
+ call pargd (0.0)
+ } else {
+ call pargd (sqrt (GM_XRMS(fit) / (ngood - 1)))
+ call pargd (sqrt (GM_YRMS(fit) / (ngood - 1)))
+ }
+ call geo_showd (res, fit, sx1, sy1, YES)
+ }
+
+ # Compute and print the fitted x and y values.
+ if (res != NULL) {
+ call geo_evald (sx1, sy1, sx2, sy2, Memd[xref], Memd[yref],
+ Memd[xfit], Memd[yfit], npts)
+ call geo_plistd (res, fit, Memd[xref], Memd[yref], Memd[xin],
+ Memd[yin], Memd[xfit], Memd[yfit], Memd[wts], npts)
+ }
+
+ # Free the data
+ if (xref != NULL)
+ call mfree (xref, TY_DOUBLE)
+ if (yref != NULL)
+ call mfree (yref, TY_DOUBLE)
+ if (xin != NULL)
+ call mfree (xin, TY_DOUBLE)
+ if (yin != NULL)
+ call mfree (yin, TY_DOUBLE)
+ if (xfit != NULL)
+ call mfree (xfit, TY_DOUBLE)
+ if (yfit != NULL)
+ call mfree (yfit, TY_DOUBLE)
+ if (wts != NULL)
+ call mfree (wts, TY_DOUBLE)
+
+ # Output the data.
+ call geo_moutd (fit, out, sx1, sy1, sx2, sy2)
+
+ # Free the space and close files.
+ call geo_mmfreed (sx1, sy1, sx2, sy2)
+ call sfree (sp)
+end
+
+
+define GEO_DEFBUFSIZE 1000 # default data buffer sizes
+
+# GEO_RDXY -- Read in the data points.
+
+int procedure geo_rdxyd (fd, xref, yref, xin, yin, xmin, xmax, ymin, ymax)
+
+int fd # the input file descriptor
+pointer xref # the x reference coordinates
+pointer yref # the y reference coordinates
+pointer xin # the x coordinates
+pointer yin # the y coordinates
+double xmin, xmax # the range of the x coordinates
+double ymin, ymax # the range of the y coordinates
+
+int npts, bufsize
+int fscan(), nscan()
+
+begin
+ bufsize = GEO_DEFBUFSIZE
+ call malloc (xref, bufsize, TY_DOUBLE)
+ call malloc (yref, bufsize, TY_DOUBLE)
+ call malloc (xin, bufsize, TY_DOUBLE)
+ call malloc (yin, bufsize, TY_DOUBLE)
+
+ npts = 0
+ while (fscan (fd) != EOF) {
+
+ # Decode the data.
+ call gargd (Memd[xref+npts])
+ call gargd (Memd[yref+npts])
+ call gargd (Memd[xin+npts])
+ call gargd (Memd[yin+npts])
+ if (nscan() < 4)
+ next
+
+ # Check the data limits.
+ if (! IS_INDEFD(xmin)) {
+ if (Memd[xref+npts] < xmin)
+ next
+ }
+ if (! IS_INDEFD(xmax)) {
+ if (Memd[xref+npts] > xmax)
+ next
+ }
+ if (! IS_INDEFD(ymin)) {
+ if (Memd[yref+npts] < ymin)
+ next
+ }
+ if (! IS_INDEFD(ymax)) {
+ if (Memd[yref+npts] > ymax)
+ next
+ }
+
+ npts = npts + 1
+ if (npts >= bufsize) {
+ bufsize = bufsize + GEO_DEFBUFSIZE
+ call realloc (xref, bufsize, TY_DOUBLE)
+ call realloc (yref, bufsize, TY_DOUBLE)
+ call realloc (xin, bufsize, TY_DOUBLE)
+ call realloc (yin, bufsize, TY_DOUBLE)
+ }
+ }
+
+ if (npts <= 0) {
+ call mfree (xref, TY_DOUBLE)
+ call mfree (yref, TY_DOUBLE)
+ call mfree (xin, TY_DOUBLE)
+ call mfree (yin, TY_DOUBLE)
+ xref = NULL
+ yref = NULL
+ xin = NULL
+ yin = NULL
+ } else if (npts < bufsize) {
+ call realloc (xref, npts, TY_DOUBLE)
+ call realloc (yref, npts, TY_DOUBLE)
+ call realloc (xin, npts, TY_DOUBLE)
+ call realloc (yin, npts, TY_DOUBLE)
+ }
+
+ return (npts)
+end
+
+
+# GEO_EVAL -- Evalute the fit.
+
+procedure geo_evald (sx1, sy1, sx2, sy2, xref, yref, xi, eta, npts)
+
+pointer sx1, sy1 #I pointer to linear surfaces
+pointer sx2, sy2 #I pointer to higher order surfaces
+double xref[ARB] #I the x reference coordinates
+double yref[ARB] #I the y reference coordinates
+double xi[ARB] #O the fitted xi coordinates
+double eta[ARB] #O the fitted eta coordinates
+int npts #I the number of points
+
+pointer sp, temp
+
+begin
+ call smark (sp)
+ call salloc (temp, npts, TY_DOUBLE)
+
+ call dgsvector (sx1, xref, yref, xi, npts)
+ if (sx2 != NULL) {
+ call dgsvector (sx2, xref, yref, Memd[temp], npts)
+ call aaddd (Memd[temp], xi, xi, npts)
+ }
+ call dgsvector (sy1, xref, yref, eta, npts)
+ if (sy2 != NULL) {
+ call dgsvector (sy2, xref, yref, Memd[temp], npts)
+
+ call aaddd (Memd[temp], eta, eta, npts)
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_MOUT -- Write the output database file.
+
+procedure geo_moutd (fit, out, sx1, sy1, sx2, sy2)
+
+pointer fit #I pointer to fitting structure
+int out #I pointer to database file
+pointer sx1, sy1 #I pointer to linear surfaces
+pointer sx2, sy2 #I pointer to distortion surfaces
+
+int i, npts, ncoeff
+pointer sp, str, xcoeff, ycoeff
+double xrms, yrms, xshift, yshift, xscale, yscale, xrot, yrot
+int dgsgeti()
+int rg_wrdstr()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Compute the x and y fit rms.
+ #npts = max (0, GM_NPTS(fit) - GM_NREJECT(fit) - GM_NWTS0(fit))
+ npts = max (0, GM_NPTS(fit) - GM_NWTS0(fit))
+ xrms = max (0.0d0, GM_XRMS(fit))
+ yrms = max (0.0d0, GM_YRMS(fit))
+ if (npts > 1) {
+ xrms = sqrt (xrms / (npts - 1))
+ yrms = sqrt (yrms / (npts - 1))
+ } else {
+ xrms = 0.0d0
+ yrms = 0.0d0
+ }
+
+ # Print title.
+ call dtptime (out)
+ call dtput (out, "begin\t%s\n")
+ call pargstr (GM_RECORD(fit))
+
+ # Print the x and y mean values.
+ call dtput (out, "\txrefmean\t%g\n")
+ call pargd (GM_XOREF(fit))
+ call dtput (out, "\tyrefmean\t%g\n")
+ call pargd (GM_YOREF(fit))
+ call dtput (out, "\txmean\t\t%g\n")
+ call pargd (GM_XOIN(fit))
+ call dtput (out, "\tymean\t\t%g\n")
+ call pargd (GM_YOIN(fit))
+
+ # Print some of the fitting parameters.
+ if (rg_wrdstr (GM_FIT(fit), Memc[str], SZ_FNAME, GM_GEOMETRIES) <= 0)
+ call strcpy ("general", Memc[str], SZ_FNAME)
+ call dtput (out, "\tgeometry\t%s\n")
+ call pargstr (Memc[str])
+ if (rg_wrdstr (GM_FUNCTION(fit), Memc[str], SZ_FNAME, GM_FUNCS) <= 0)
+ call strcpy ("polynomial", Memc[str], SZ_FNAME)
+ call dtput (out, "\tfunction\t%s\n")
+ call pargstr (Memc[str])
+
+ # Output the geometric parameters.
+ call geo_lcoeffd (sx1, sy1, xshift, yshift, xscale, yscale, xrot, yrot)
+ call dtput (out, "\txshift\t\t%g\n")
+ call pargd (xshift)
+ call dtput (out, "\tyshift\t\t%g\n")
+ call pargd (yshift)
+ call dtput (out, "\txmag\t\t%g\n")
+ call pargd (xscale)
+ call dtput (out, "\tymag\t\t%g\n")
+ call pargd (yscale)
+ call dtput (out, "\txrotation\t%g\n")
+ call pargd (xrot)
+ call dtput (out, "\tyrotation\t%g\n")
+ call pargd (yrot)
+
+ # Out the rms values.
+ call dtput (out, "\txrms\t\t%g\n")
+ call pargd (double(xrms))
+ call dtput (out, "\tyrms\t\t%g\n")
+ call pargd (double(yrms))
+
+ # Allocate memory for linear coefficients.
+ ncoeff = max (dgsgeti (sx1, GSNSAVE), dgsgeti (sy1, GSNSAVE))
+ call calloc (xcoeff, ncoeff, TY_DOUBLE)
+ call calloc (ycoeff, ncoeff, TY_DOUBLE)
+
+ # Output the linear coefficients.
+ call dgssave (sx1, Memd[xcoeff])
+ call dgssave (sy1, Memd[ycoeff])
+ call dtput (out, "\tsurface1\t%d\n")
+ call pargi (ncoeff)
+ do i = 1, ncoeff {
+ call dtput (out, "\t\t\t%g\t%g\n")
+ call pargd (Memd[xcoeff+i-1])
+ call pargd (Memd[ycoeff+i-1])
+ }
+
+ call mfree (xcoeff, TY_DOUBLE)
+ call mfree (ycoeff, TY_DOUBLE)
+
+ # Allocate memory for higer order coefficients.
+ if (sx2 == NULL)
+ ncoeff = 0
+ else
+ ncoeff = dgsgeti (sx2, GSNSAVE)
+ if (sy2 == NULL)
+ ncoeff = max (0, ncoeff)
+ else
+ ncoeff = max (dgsgeti (sy2, GSNSAVE), ncoeff)
+ call calloc (xcoeff, ncoeff, TY_DOUBLE)
+ call calloc (ycoeff, ncoeff, TY_DOUBLE)
+
+ # Save the coefficients.
+ call dgssave (sx2, Memd[xcoeff])
+ call dgssave (sy2, Memd[ycoeff])
+
+ # Output the coefficients.
+ call dtput (out, "\tsurface2\t%d\n")
+ call pargi (ncoeff)
+ do i = 1, ncoeff {
+ call dtput (out, "\t\t\t%g\t%g\n")
+ call pargd (Memd[xcoeff+i-1])
+ call pargd (Memd[ycoeff+i-1])
+ }
+
+ # Cleanup.
+ call mfree (xcoeff, TY_DOUBLE)
+ call mfree (ycoeff, TY_DOUBLE)
+ call sfree (sp)
+end
+
+
+# GEO_PLIST -- Print the input, output, and fitted data and the residuals.
+
+procedure geo_plistd (fd, fit, xref, yref, xin, yin, xfit, yfit, wts, npts)
+
+int fd #I the results file descriptor
+pointer fit #I pointer to the fit structure
+double xref[ARB] #I the input x coordinates
+double yref[ARB] #I the input y coordinates
+double xin[ARB] #I the input ra / longitude coordinates
+double yin[ARB] #I the input dec / latitude coordinates
+double xfit[ARB] #I the fitted ra / longitude coordinates
+double yfit[ARB] #I the fitted dec / latitude coordinates
+double wts[ARB] #I the weights array
+int npts #I the number of data points
+
+int i, index
+pointer sp, fmtstr, twts
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (fmtstr, SZ_LINE, TY_CHAR)
+ call salloc (twts, npts, TY_DOUBLE)
+
+ # Compute the weights.
+ call amovd (wts, Memd[twts], npts)
+ do i = 1, GM_NREJECT(fit) {
+ index = Memi[GM_REJ(fit)+i-1]
+ if (wts[index] > double(0.0))
+ Memd[twts+index-1] = double(0.0)
+ }
+
+ # Print banner.
+ call fprintf (fd, "\n# Input Coordinate Listing\n")
+ call fprintf (fd, "# Column 1: X (reference) \n")
+ call fprintf (fd, "# Column 2: Y (reference)\n")
+ call fprintf (fd, "# Column 3: X (input)\n")
+ call fprintf (fd, "# Column 4: Y (input)\n")
+ call fprintf (fd, "# Column 5: X (fit)\n")
+ call fprintf (fd, "# Column 6: Y (fit)\n")
+ call fprintf (fd, "# Column 7: X (residual)\n")
+ call fprintf (fd, "# Column 8: Y (residual)\n\n")
+
+ # Create the format string.
+ call sprintf (Memc[fmtstr], SZ_LINE, "%s %s %s %s %s %s %s %s\n")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+ call pargstr ("%16.14g")
+
+ # Print the data.
+ do i = 1, npts {
+ call fprintf (fd, Memc[fmtstr])
+ call pargd (xref[i])
+ call pargd (yref[i])
+ call pargd (xin[i])
+ call pargd (yin[i])
+ if (Memd[twts+i-1] > 0.0d0) {
+ call pargd (xfit[i])
+ call pargd (yfit[i])
+ call pargd (xin[i] - xfit[i])
+ call pargd (yin[i] - yfit[i])
+ } else {
+ call pargd (INDEFD)
+ call pargd (INDEFD)
+ call pargd (INDEFD)
+ call pargd (INDEFD)
+ }
+
+ }
+
+ call fprintf (fd, "\n")
+
+ call sfree (sp)
+
+end
+
+# GEO_SHOW -- Print the coordinate mapping parameters.
+
+procedure geo_showd (fd, fit, sx1, sy1, comment)
+
+int fd #I the output file descriptor
+pointer fit #I pointer to the fit structure
+pointer sx1, sy1 #I pointer to linear surfaces
+int comment #I comment the output ?
+
+double xshift, yshift, a, b, c, d
+double xscale, yscale, xrot, yrot
+pointer sp, str
+bool fp_equald()
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Compute the geometric parameters.
+ call geo_gcoeffd (sx1, sy1, xshift, yshift, a, b, c, d)
+
+ if (comment == NO) {
+ call fprintf (fd, "Coordinate mapping parameters\n")
+ } else {
+ call fprintf (fd, "# Coordinate mapping parameters\n")
+ }
+
+ if (comment == NO) {
+ call fprintf (fd,
+ " Mean Xref and Yref: %0.7g %0.7g\n")
+ call pargd (GM_XOREF(fit))
+ call pargd (GM_YOREF(fit))
+ call fprintf (fd,
+ " Mean Xin and Yin: %0.7g %0.7g\n")
+ call pargd (GM_XOIN(fit))
+ call pargd (GM_YOIN(fit))
+ call fprintf (fd,
+ " X and Y shift: %0.7g %0.7g (xin yin)\n")
+ call pargd (xshift)
+ call pargd (yshift)
+ } else {
+ call fprintf (fd,
+ "# Mean Xref and Yref: %0.7g %0.7g\n")
+ call pargd (GM_XOREF(fit))
+ call pargd (GM_YOREF(fit))
+ call fprintf (fd,
+ "# Mean Xin and Yin: %0.7g %g0.7\n")
+ call pargd (GM_XOIN(fit))
+ call pargd (GM_YOIN(fit))
+ call fprintf (fd,
+ "# X and Y shift: %0.7g %0.7g (xin yin)\n")
+ call pargd (xshift)
+ call pargd (yshift)
+ }
+
+ # Output the scale factors.
+ xscale = sqrt (a * a + c * c)
+ yscale = sqrt (b * b + d * d)
+ if (comment == NO) {
+ call fprintf (fd,
+ " X and Y scale: %0.7g %0.7g (xin / xref yin / yref)\n")
+ call pargd (xscale)
+ call pargd (yscale)
+ } else {
+ call fprintf (fd,
+ "# X and Y scale: %0.7g %0.7g (xin / xref yin / yref)\n")
+ call pargd (xscale)
+ call pargd (yscale)
+ }
+
+ # Output the rotation factors.
+ if (fp_equald (a, double(0.0)) && fp_equald (c, double(0.0)))
+ xrot = double(0.0)
+ else
+ xrot = RADTODEG (atan2 (-c, a))
+ if (xrot < double(0.0))
+ xrot = xrot + double(360.0)
+ if (fp_equald (b, double(0.0)) && fp_equald (d, double(0.0)))
+ yrot = double(0.0)
+ else
+ yrot = RADTODEG (atan2 (b, d))
+ if (yrot < double(0.0))
+ yrot = yrot + double(360.0)
+ if (comment == NO) {
+ call fprintf (fd,
+ " X and Y axis rotation: %0.5f %0.5f (degrees degrees)\n")
+ call pargd (xrot)
+ call pargd (yrot)
+ } else {
+ call fprintf (fd,
+ "# X and Y axis rotation: %0.5f %0.5f (degrees degrees)\n")
+ call pargd (xrot)
+ call pargd (yrot)
+ }
+
+ call sfree (sp)
+end
+
+
diff --git a/pkg/images/immatch/src/geometry/t_geotran.x b/pkg/images/immatch/src/geometry/t_geotran.x
new file mode 100644
index 00000000..5e5cd2e3
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/t_geotran.x
@@ -0,0 +1,880 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mwset.h>
+include <math.h>
+include <math/gsurfit.h>
+include "geotran.h"
+
+# T_GEOTRAN -- Geometrically transform a list of images either linearly or
+# using a transformation computed by the GEOMAP task.
+
+procedure t_geotran ()
+
+int ncols, nlines # output picture size
+real xmin, xmax, ymin, ymax # minimum and maximum ref values
+real xscale, yscale # output picture scale
+real xin, yin # input picture origin
+real xshift, yshift # x and y shifts
+real xout, yout # output picture origin
+real xmag, ymag # input picture scale
+real xrotation, yrotation # rotation angle
+int nxblock, nyblock # block size of image to be used
+
+bool verbose
+int list1, list2, tflist, ndim, nc, nl, mode
+pointer sp, imtlist1, imtlist2, database, transform, record
+pointer image1, image2, imtemp, imroot, section, str
+pointer geo, sx1, sy1, sx2, sy2, in, out, mw
+real xs, ys, txshift, tyshift, txmag, tymag, txrot, tyrot
+double oltv[2], nltv[2], oltm[2,2], nltm[2,2]
+
+bool clgetb(), envgetb(), streq()
+int imtopen(), imtlen(), clgeti(), imtgetim(), clgwrd(), btoi()
+pointer immap(), mw_openim()
+real clgetr()
+errchk immap()
+
+begin
+ # Set up the geotran structure.
+ call smark (sp)
+ call salloc (imtlist1, SZ_LINE, TY_CHAR)
+ call salloc (imtlist2, SZ_LINE, TY_CHAR)
+ call salloc (database, SZ_FNAME, TY_CHAR)
+ call salloc (transform, SZ_FNAME, TY_CHAR)
+ call salloc (record, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call salloc (image2, SZ_FNAME, TY_CHAR)
+ call salloc (imtemp, SZ_FNAME, TY_CHAR)
+ call salloc (imroot, SZ_FNAME, TY_CHAR)
+ call salloc (section, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (geo, LEN_GEOSTRUCT, TY_STRUCT)
+
+ # Get the input and output lists and database file.
+ call clgstr ("input", Memc[imtlist1], SZ_FNAME)
+ call clgstr ("output", Memc[imtlist2], SZ_FNAME)
+ call clgstr ("database", Memc[database], SZ_FNAME)
+ if (Memc[database] != EOS) {
+ call clgstr ("transforms", Memc[transform], SZ_FNAME)
+ tflist = imtopen (Memc[transform])
+ GT_GEOMODE(geo) = clgwrd ("geometry", Memc[str], SZ_LINE,
+ ",junk,linear,distortion,geometric,")
+ } else {
+ tflist = NULL
+ GT_GEOMODE(geo) = GT_NONE
+ }
+
+ # Get the output picture format parameters.
+ xmin = clgetr ("xmin")
+ xmax = clgetr ("xmax")
+ ymin = clgetr ("ymin")
+ ymax = clgetr ("ymax")
+ xscale = clgetr ("xscale")
+ yscale = clgetr ("yscale")
+ ncols= clgeti ("ncols")
+ nlines = clgeti ("nlines")
+
+ # Get the geometric transformation parameters.
+ xin = clgetr ("xin")
+ yin = clgetr ("yin")
+ xshift = clgetr ("xshift")
+ yshift = clgetr ("yshift")
+ xout = clgetr ("xout")
+ yout = clgetr ("yout")
+ xmag = clgetr ("xmag")
+ ymag = clgetr ("ymag")
+ xrotation = clgetr ("xrotation")
+ yrotation = clgetr ("yrotation")
+
+ # Get the interpolation parameters.
+ call clgstr ("interpolant", GT_INTERPSTR(geo), SZ_FNAME)
+ #GT_INTERPOLANT(geo) = clgwrd ("interpolant", Memc[str], SZ_LINE,
+ #",nearest,linear,poly3,poly5,spline3,")
+ GT_BOUNDARY(geo) = clgwrd ("boundary", Memc[str], SZ_LINE,
+ ",constant,nearest,reflect,wrap,")
+ GT_CONSTANT(geo) = clgetr ("constant")
+ GT_XSAMPLE(geo) = clgetr ("xsample")
+ GT_YSAMPLE(geo) = clgetr ("ysample")
+ GT_FLUXCONSERVE(geo) = btoi (clgetb("fluxconserve"))
+
+ nxblock = clgeti ("nxblock")
+ nyblock = clgeti ("nyblock")
+ verbose = clgetb ("verbose")
+
+ # Open the lists of images and check the scale lengths.
+ list1 = imtopen (Memc[imtlist1])
+ list2 = imtopen (Memc[imtlist2])
+ if (imtlen (list1) != imtlen (list2)) {
+ call imtclose (list1)
+ call imtclose (list2)
+ if (tflist != NULL)
+ call imtclose (tflist)
+ call error (0, "Input and output lists not the same length.")
+ }
+
+ # Check the transform list.
+ if (tflist != NULL) {
+ if (imtlen (tflist) > 1 && imtlen (tflist) != imtlen (list1)) {
+ call imtclose (list1)
+ call imtclose (list2)
+ call imtclose (tflist)
+ call error (0, "Transform and input lists not the same length.")
+ }
+ }
+
+ # Loop over the images.
+ if (verbose) {
+ call printf ("\n")
+ }
+ while (imtgetim (list1, Memc[image1], SZ_FNAME) != EOF &&
+ imtgetim (list2, Memc[image2], SZ_FNAME) != EOF) {
+
+ # Print messages.
+ if (verbose) {
+ call printf ("Transforming image %s to image %s\n")
+ call pargstr (Memc[image1])
+ call pargstr (Memc[image2])
+ call flush (STDOUT)
+ }
+
+ # Open the images.
+ in = immap (Memc[image1], READ_ONLY, 0)
+ call imgimage (Memc[image1], Memc[str], SZ_FNAME)
+ call imgimage (Memc[image2], Memc[imroot], SZ_FNAME)
+ call imgsection (Memc[image2], Memc[section], SZ_FNAME)
+ if (streq (Memc[str], Memc[imroot])) {
+ call strcpy (Memc[imroot], Memc[imtemp], SZ_FNAME)
+ call mktemp ("tmp", Memc[image2], SZ_FNAME)
+ } else
+ call strcpy (Memc[image2], Memc[imtemp], SZ_FNAME)
+ ifnoerr (out = immap (Memc[image2], READ_WRITE, 0)) {
+ mode = READ_WRITE
+ nc = IM_LEN(out,1)
+ nl = IM_LEN(out,2)
+ xs = INDEF
+ ys = INDEF
+ } else if (Memc[section] != EOS) {
+ mode = NEW_IMAGE
+ out = immap (Memc[imroot], NEW_IMAGE, 0)
+ IM_NDIM(out) = IM_NDIM(in)
+ if (IS_INDEFI(ncols))
+ IM_LEN(out,1) = IM_LEN(in,1)
+ else
+ IM_LEN(out,1) = ncols
+ if (IS_INDEFI(nlines))
+ IM_LEN(out,2) = IM_LEN(in,2)
+ else
+ IM_LEN(out,2) = nlines
+ IM_PIXTYPE(out) = IM_PIXTYPE(in)
+ call geo_imzero (out, GT_CONSTANT(geo))
+ call imunmap (out)
+ out = immap (Memc[image2], READ_WRITE, 0)
+ nc = IM_LEN(out,1)
+ nl = IM_LEN(out,2)
+ xs = INDEF
+ ys = INDEF
+ } else {
+ mode = NEW_COPY
+ out = immap (Memc[image2], NEW_COPY, in)
+ nc = ncols
+ nl = nlines
+ xs = xscale
+ ys = yscale
+ }
+
+ # Set the geometry parameters.
+ call geo_set (geo, xmin, xmax, ymin, ymax, xs, ys, nc, nl, xin,
+ yin, xshift, yshift, xout, yout, xmag, ymag, xrotation,
+ yrotation)
+
+ # Get the coordinate surfaces.
+ if (GT_GEOMODE(geo) == GT_NONE) {
+ call geo_format (in, out, geo, sx1, sy1, sx2, sy2)
+ if (verbose) {
+ call geo_lcoeffr (sx1, sy1, txshift, tyshift, txmag,
+ tymag, txrot, tyrot)
+ call printf (" xshift: %.2f yshift: %.2f ")
+ call pargr (txshift)
+ call pargr (tyshift)
+ call printf ("xmag: %.2f ymag: %.2f ")
+ call pargr (txmag)
+ call pargr (tymag)
+ call printf ("xrot: %.2f yrot: %.2f\n")
+ call pargr (txrot)
+ call pargr (tyrot)
+ call flush (STDOUT)
+ }
+ } else {
+ if (imtgetim (tflist, Memc[str], SZ_FNAME) != EOF)
+ call strcpy (Memc[str], Memc[record], SZ_FNAME)
+ call geo_dformat (in, out, geo, Memc[database], Memc[record],
+ sx1, sy1, sx2, sy2)
+ if (verbose) {
+ call printf (" Using transform %s in database %s\n")
+ call pargstr (Memc[record])
+ call pargstr (Memc[database])
+ call flush (STDOUT)
+ }
+ }
+
+ # Transform the image.
+ if (IM_LEN(out,1) <= nxblock && IM_LEN(out,2) <= nyblock) {
+ if (GT_XSAMPLE(geo) > 1.0 || GT_YSAMPLE(geo) > 1.0)
+ call geo_simtran (in, out, geo, sx1, sy1, sx2, sy2)
+ else
+ call geo_imtran (in, out, geo, sx1, sy1, sx2, sy2)
+ } else {
+ if (GT_XSAMPLE(geo) > 1.0 || GT_YSAMPLE(geo) > 1.0) {
+ if (IM_NDIM(out) == 1)
+ call geo_stran (in, out, geo, sx1, sy1, sx2, sy2,
+ nxblock, 1)
+ else
+ call geo_stran (in, out, geo, sx1, sy1, sx2, sy2,
+ nxblock, nyblock)
+ } else {
+ if (IM_NDIM(out) == 1)
+ call geo_tran (in, out, geo, sx1, sy1, sx2, sy2,
+ nxblock, 1)
+ else
+ call geo_tran (in, out, geo, sx1, sy1, sx2, sy2,
+ nxblock, nyblock)
+ }
+ }
+
+ # Update the linear part of the wcs.
+ if (!envgetb ("nomwcs") && mode == NEW_COPY) {
+ ndim = IM_NDIM(in)
+ mw = mw_openim (in)
+ call geo_gwcs (geo, sx1, sy1, oltm, oltv)
+ call mw_invertd (oltm, nltm, ndim)
+ call mw_vmuld (nltm, oltv, nltv, ndim)
+ call anegd (nltv, nltv, ndim)
+ call geo_swcs (mw, nltm, nltv, ndim)
+ call mw_saveim (mw, out)
+ call mw_close (mw)
+ }
+
+ # Free the surfaces.
+ call gsfree (sx1)
+ call gsfree (sy1)
+ call gsfree (sx2)
+ call gsfree (sy2)
+
+ # Close the images.
+ call imunmap (in)
+ call imunmap (out)
+
+ call xt_delimtemp (Memc[image2], Memc[imtemp])
+ }
+
+ # Clean up.
+ call sfree (sp)
+ if (tflist != NULL)
+ call imtclose (tflist)
+ call imtclose (list1)
+ call imtclose (list2)
+end
+
+
+# GEO_IMZERO -- Create a dummy output image filled with the constant boundary
+# extension value.
+
+procedure geo_imzero (im, constant)
+
+pointer im #I pointer to the input image
+real constant #I the constant value to insert in the imagw
+
+int npix
+pointer sp, v, buf
+int impnls(), impnll(), impnlr(), impnld(), impnlx()
+
+begin
+ # Setup start vector for sequential reads and writes.
+ call smark (sp)
+ call salloc (v, IM_MAXDIM, TY_LONG)
+ call amovkl (long(1), Meml[v], IM_MAXDIM)
+
+ # Initialize the image.
+ npix = IM_LEN(im, 1)
+ switch (IM_PIXTYPE(im)) {
+ case TY_SHORT:
+ while (impnls (im, buf, Meml[v]) != EOF)
+ call amovks (short (constant), Mems[buf], npix)
+ case TY_USHORT, TY_INT, TY_LONG:
+ while (impnll (im, buf, Meml[v]) != EOF)
+ call amovkl (long (constant), Meml[buf], npix)
+ case TY_REAL:
+ while (impnlr (im, buf, Meml[v]) != EOF)
+ call amovkr (constant, Memr[buf], npix)
+ case TY_DOUBLE:
+ while (impnld (im, buf, Meml[v]) != EOF)
+ call amovkd (double (constant), Memd[buf], npix)
+ case TY_COMPLEX:
+ while (impnlx (im, buf, Meml[v]) != EOF)
+ call amovkx (complex (constant, 0.0), Memx[buf], npix)
+ default:
+ call error (1, "Unknown pixel datatype")
+ }
+
+ call sfree (sp)
+end
+
+
+# GEO_SET -- Set the image dependent task parameters individually for each
+# image.
+
+procedure geo_set (geo, xmin, xmax, ymin, ymax, xscale, yscale, ncols, nlines,
+ xin, yin, xshift, yshift, xout, yout, xmag, ymag, xrotation, yrotation)
+
+pointer geo #I pointer to geotran structure
+real xmin, xmax #I minimum and maximum reference values
+real ymin, ymax #I minimum and maximum reference values
+real xscale, yscale #I output picture scale
+int ncols, nlines #I output picture size
+real xin, yin #I input picture pixel coordinates
+real xshift, yshift #I shift of origin
+real xout, yout #I corresponding output picture coords
+real xmag, ymag #I input picture scale
+real xrotation, yrotation #I scale angle
+
+begin
+ # Set the output picture format parameters.
+ GT_XMIN(geo) = xmin
+ GT_XMAX(geo) = xmax
+ GT_YMIN(geo) = ymin
+ GT_YMAX(geo) = ymax
+ GT_XSCALE(geo) = xscale
+ GT_YSCALE(geo) = yscale
+ GT_NCOLS(geo) = ncols
+ GT_NLINES(geo) = nlines
+
+ # Set the transformation parameters.
+ GT_XIN(geo) = xin
+ GT_YIN(geo) = yin
+ GT_XSHIFT(geo) = xshift
+ GT_YSHIFT(geo) = yshift
+ GT_XOUT(geo) = xout
+ GT_YOUT(geo) = yout
+ GT_XMAG(geo) = xmag
+ GT_YMAG(geo) = ymag
+ GT_XROTATION(geo) = xrotation
+ GT_YROTATION(geo) = yrotation
+end
+
+
+# GEO_FORMAT -- Format the output picture when there is no database file.
+
+procedure geo_format (in, out, geo, sx1, sy1, sx2, sy2)
+
+pointer in #I pointer to the input image
+pointer out #I pointer to the ouput image
+pointer geo #I pointer to the geotran structure
+pointer sx1, sy1 #O pointer to linear surfaces
+pointer sx2, sy2 #O pointer to distortion surfaces
+
+real xmax, ymax
+
+begin
+ # Get the scale transformation parameters.
+ if (IS_INDEFR(GT_XMAG(geo)))
+ GT_XMAG(geo) = 1.
+ if (IM_NDIM(in) == 1)
+ GT_YMAG(geo) = 1.
+ else if (IS_INDEFR(GT_YMAG(geo)))
+ GT_YMAG(geo) = 1.
+
+ # Get the rotate transformation parameters.
+ if (IM_NDIM(in) == 1)
+ GT_XROTATION(geo) = DEGTORAD(0.)
+ else if (IS_INDEFR(GT_XROTATION(geo)))
+ GT_XROTATION(geo) = DEGTORAD(0.)
+ else
+ GT_XROTATION(geo) = DEGTORAD(GT_XROTATION(geo))
+ if (IM_NDIM(in) == 1)
+ GT_YROTATION(geo) = DEGTORAD(0.)
+ else if (IS_INDEFR(GT_YROTATION(geo)))
+ GT_YROTATION(geo) = DEGTORAD(0.)
+ else
+ GT_YROTATION(geo) = DEGTORAD(GT_YROTATION(geo))
+
+ # Automatically compute the maximum extent of the image.
+ if (GT_XMAX(geo) <= 0.0 || GT_YMAX(geo) <= 0.0) {
+
+ # Compute the size of the output image.
+ xmax = abs (cos(GT_XROTATION(geo)) * IM_LEN(in,1) /
+ GT_XMAG(geo)) + abs(sin(GT_YROTATION(geo)) * IM_LEN(in,2) /
+ GT_YMAG(geo))
+ ymax = abs (sin(GT_XROTATION(geo)) * IM_LEN(in, 1) /
+ GT_XMAG(geo)) + abs (cos(GT_YROTATION(geo)) * IM_LEN(in,2) /
+ GT_YMAG(geo))
+ }
+
+ # Set up the x reference coordinate limits.
+ if (IS_INDEF(GT_XMIN(geo)))
+ GT_XMIN(geo) = 1.
+ else
+ GT_XMIN(geo) = max (1.0, GT_XMIN(geo))
+ if (IS_INDEF(GT_XMAX(geo)))
+ GT_XMAX(geo) = IM_LEN(in,1)
+ else if (GT_XMAX(geo) <= 0.0)
+ #GT_XMAX(geo) = int (xmax + 1.0)
+ GT_XMAX(geo) = xmax
+
+ # Set up the y reference coordinate limits.
+ if (IS_INDEF(GT_YMIN(geo)))
+ GT_YMIN(geo) = 1.
+ else
+ GT_YMIN(geo) = max (1.0, GT_YMIN(geo))
+ if (IS_INDEF(GT_YMAX(geo)))
+ GT_YMAX(geo) = IM_LEN(in, 2)
+ else if (GT_YMAX(geo) <= 0.0)
+ #GT_YMAX(geo) = int (ymax + 1.0)
+ GT_YMAX(geo) = ymax
+
+ # Set the number of columns and rows.
+ if (IS_INDEFI(GT_NCOLS(geo)))
+ GT_NCOLS(geo) = IM_LEN(in, 1)
+ if (IM_NDIM(in) == 1)
+ GT_NLINES(geo) = 1
+ else if (IS_INDEFI(GT_NLINES(geo)))
+ GT_NLINES(geo) = IM_LEN(in, 2)
+
+ # Set scale, overiding number of columns and rows if necessary.
+ if (IS_INDEFR(GT_XSCALE(geo)))
+ GT_XSCALE(geo) = (GT_XMAX(geo) - GT_XMIN(geo)) / (GT_NCOLS(geo) - 1)
+ else
+ GT_NCOLS(geo) = (GT_XMAX(geo) - GT_XMIN(geo)) / GT_XSCALE(geo) + 1
+ if (IM_NDIM(in) == 1)
+ GT_YSCALE(geo) = 1.0
+ else if (IS_INDEFR(GT_YSCALE(geo)))
+ GT_YSCALE(geo) = (GT_YMAX(geo) - GT_YMIN(geo)) /
+ (GT_NLINES(geo) - 1)
+ else
+ GT_NLINES(geo) = (GT_YMAX(geo) - GT_YMIN(geo)) / GT_YSCALE(geo) + 1
+ IM_LEN(out, 1) = GT_NCOLS(geo)
+ IM_LEN(out, 2) = GT_NLINES(geo)
+
+ # Set up the surfaces, distortion surfaces are NULL.
+ if (IM_NDIM(in) == 1) {
+ call gsinit (sx1, GS_POLYNOMIAL, 2, 2, GS_XNONE, GT_XMIN(geo),
+ GT_XMAX(geo), 0.5, 1.5)
+ call gsinit (sy1, GS_POLYNOMIAL, 2, 2, GS_XNONE, GT_XMIN(geo),
+ GT_XMAX(geo), 0.5, 1.5)
+ } else {
+ call gsinit (sx1, GS_POLYNOMIAL, 2, 2, GS_XNONE, GT_XMIN(geo),
+ GT_XMAX(geo), GT_YMIN(geo), GT_YMAX(geo))
+ call gsinit (sy1, GS_POLYNOMIAL, 2, 2, GS_XNONE, GT_XMIN(geo),
+ GT_XMAX(geo), GT_YMIN(geo), GT_YMAX(geo))
+ }
+ sx2 = NULL
+ sy2 = NULL
+
+ # Adjust rotation, x and y scale, scale angle, and flip.
+ call geo_rotmagr (sx1, sy1, GT_XMAG(geo), GT_YMAG(geo),
+ GT_XROTATION(geo), GT_YROTATION(geo))
+
+ # Adjust the shift.
+ call geo_shift (in, out, geo, sx1, sy1)
+end
+
+
+# GEO_DFORMAT -- Get the coordinate transformation from a database file.
+
+procedure geo_dformat (in, out, geo, database, transform, sx1, sy1, sx2, sy2)
+
+pointer in, out #I pointers to input and output images
+pointer geo #I pointer to geotran structure
+char database[ARB] #I name of database file
+char transform[ARB] #I name of transform
+pointer sx1, sy1 #O pointer to linear part of surface fit
+pointer sx2, sy2 #O pointer to higher order surface
+
+int i, dt, rec, ncoeff, junk
+pointer xcoeff, ycoeff, newsx1, newsy1
+int dtmap(), dtlocate(), dtgeti(), dtscan()
+errchk gsrestore
+
+begin
+ # Map the database and locate the transformation record.
+ dt = dtmap (database, READ_ONLY)
+ rec = dtlocate (dt, transform)
+
+ # Get the linear part of the fit.
+ ncoeff = dtgeti (dt, rec, "surface1")
+ call malloc (xcoeff, ncoeff, TY_REAL)
+ call malloc (ycoeff, ncoeff, TY_REAL)
+ do i = 1, ncoeff {
+ junk = dtscan (dt)
+ call gargr (Memr[xcoeff+i-1])
+ call gargr (Memr[ycoeff+i-1])
+ }
+ call gsrestore (sx1, Memr[xcoeff])
+ call gsrestore (sy1, Memr[ycoeff])
+
+ # Set the output image format parameters.
+ call geo_dout (in, out, geo, sx1, sy1)
+
+ # Adjust the linear part of the fit.
+ call gscopy (sx1, newsx1)
+ call gscopy (sy1, newsy1)
+ if (GT_GEOMODE(geo) == GT_DISTORT)
+ call geo_rotmagr (newsx1, newsy1, 1.0, 1.0, 0.0, 0.0)
+ else if (! IS_INDEFR(GT_XMAG(geo)) || ! IS_INDEFR(GT_YMAG(geo)) ||
+ ! IS_INDEFR(GT_XROTATION(geo)) || ! IS_INDEFR(GT_YROTATION(geo)))
+ call geo_dcoeff (geo, dt, rec, newsx1, newsy1)
+ call geo_dshift (in, out, dt, rec, geo, newsx1, newsy1)
+
+ # Get the higher order part of the fit.
+ ncoeff = dtgeti (dt, rec, "surface2")
+ if (ncoeff > 0 && (GT_GEOMODE(geo) == GT_GEOMETRIC || GT_GEOMODE(geo) ==
+ GT_DISTORT)) {
+
+ # Get the distortion coefficients.
+ call realloc (xcoeff, ncoeff, TY_REAL)
+ call realloc (ycoeff, ncoeff, TY_REAL)
+ do i = 1, ncoeff {
+ junk = dtscan(dt)
+ call gargr (Memr[xcoeff+i-1])
+ call gargr (Memr[ycoeff+i-1])
+ }
+ iferr {
+ call gsrestore (sx2, Memr[xcoeff])
+ } then {
+ call mfree (sx2, TY_STRUCT)
+ sx2 = NULL
+ }
+ iferr {
+ call gsrestore (sy2, Memr[ycoeff])
+ } then {
+ call mfree (sy2, TY_STRUCT)
+ sy2 = NULL
+ }
+
+ } else {
+
+ sx2 = NULL
+ sy2 = NULL
+ }
+
+ # Redefine the surfaces.
+ call gsfree (sx1)
+ call gscopy (newsx1, sx1)
+ call gsfree (newsx1)
+ call gsfree (sy1)
+ call gscopy (newsy1, sy1)
+ call gsfree (newsy1)
+
+ # Cleanup.
+ call mfree (xcoeff, TY_REAL)
+ call mfree (ycoeff, TY_REAL)
+ call dtunmap (dt)
+end
+
+
+# GEO_DOUT -- Set the output image format using information in the database
+# file.
+
+procedure geo_dout (in, out, geo, sx1, sy1)
+
+pointer in, out #I pointers to input and output image
+pointer geo #I pointer to geotran sturcture
+pointer sx1, sy1 #I pointers to linear surface descriptors
+
+real gsgetr ()
+
+begin
+ # Set the reference coordinate limits.
+ if (IS_INDEFR(GT_XMIN(geo)))
+ GT_XMIN(geo) = gsgetr (sx1, GSXMIN)
+ if (IS_INDEFR(GT_XMAX(geo)))
+ GT_XMAX(geo) = gsgetr (sx1, GSXMAX)
+ if (IS_INDEFR(GT_YMIN(geo)))
+ GT_YMIN(geo) = gsgetr (sy1, GSYMIN)
+ if (IS_INDEFR(GT_YMAX(geo)))
+ GT_YMAX(geo) = gsgetr (sy1, GSYMAX)
+
+ # Set the number of lines and columns.
+ if (IS_INDEFI(GT_NCOLS(geo)))
+ GT_NCOLS(geo) = IM_LEN(in, 1)
+ if (IM_NDIM(in) == 1)
+ GT_NLINES(geo) = 1
+ else if (IS_INDEFI(GT_NLINES(geo)))
+ GT_NLINES(geo) = IM_LEN(in, 2)
+
+ # Set scale, overiding the number of columns and rows if necessary.
+ if (IS_INDEFR(GT_XSCALE(geo)))
+ GT_XSCALE(geo) = (GT_XMAX(geo) - GT_XMIN(geo)) / (GT_NCOLS(geo) - 1)
+ else
+ GT_NCOLS(geo) = abs ((GT_XMAX(geo) - GT_XMIN(geo)) /
+ GT_XSCALE(geo)) + 1
+ if (IM_NDIM(in) == 1)
+ GT_YSCALE(geo) = 1.0
+ else if (IS_INDEFR(GT_YSCALE(geo)))
+ GT_YSCALE(geo) = (GT_YMAX(geo) - GT_YMIN(geo)) /
+ (GT_NLINES(geo) - 1)
+ else
+ GT_NLINES(geo) = abs ((GT_YMAX(geo) - GT_YMIN(geo)) /
+ GT_YSCALE(geo)) + 1
+
+ # Set the output image size.
+ IM_LEN(out,1) = GT_NCOLS(geo)
+ IM_LEN(out,2) = GT_NLINES(geo)
+end
+
+
+# GEO_DSHIFT -- Adjust the shifts using information in the database file.
+
+procedure geo_dshift (in, out, dt, rec, geo, sx1, sy1)
+
+pointer in, out #I pointer to input and output images
+pointer dt #I pointer to database
+int rec #I pointer to database record
+pointer geo #I pointer to geotran structure
+pointer sx1, sy1 #U pointers to linear surfaces
+
+real gseval()
+
+begin
+ # Define the output origin.
+ if (IS_INDEFR(GT_XOUT(geo)))
+ GT_XOUT(geo) = (GT_XMAX(geo) + GT_XMIN(geo)) / 2.0
+ if (IS_INDEFR(GT_YOUT(geo)))
+ GT_YOUT(geo) = (GT_YMAX(geo) + GT_YMIN(geo)) / 2.0
+
+ # Define the input image origin.
+ if (IS_INDEFR(GT_XIN(geo)))
+ GT_XIN(geo) = gseval (sx1, GT_XOUT(geo), GT_YOUT(geo))
+ if (IS_INDEFR(GT_YIN(geo)))
+ GT_YIN(geo) = gseval (sy1, GT_XOUT(geo), GT_YOUT(geo))
+
+ # Define the shifts.
+ if (IS_INDEFR(GT_XSHIFT(geo)))
+ GT_XSHIFT(geo) = GT_XIN(geo) - gseval (sx1, GT_XOUT(geo),
+ GT_YOUT(geo))
+ if (IS_INDEFR(GT_YSHIFT(geo)))
+ GT_YSHIFT(geo) = GT_YIN(geo) - gseval (sy1, GT_XOUT(geo),
+ GT_YOUT(geo))
+
+ # Correct the coefficients.
+ call geo_xyshiftr (sx1, sy1, GT_XSHIFT(geo), GT_YSHIFT(geo))
+end
+
+
+# GEO_SHIFT -- Compute the shift.
+
+procedure geo_shift (in, out, geo, sx1, sy1)
+
+pointer in, out #I pointer to input and output images
+pointer geo #I pointer to geotran structure
+pointer sx1, sy1 #I pointers to linear surfaces
+
+real gseval()
+
+begin
+ # Determine the output origin.
+ if (IS_INDEFR(GT_XOUT(geo)))
+ GT_XOUT(geo) = (GT_XMAX(geo) + GT_XMIN(geo)) / 2.0
+ if (IS_INDEFR(GT_YOUT(geo)))
+ GT_YOUT(geo) = (GT_YMAX(geo) + GT_YMIN(geo)) / 2.0
+
+ # Determine the input origin.
+ if (IS_INDEFR(GT_XIN(geo)))
+ GT_XIN(geo) = (real (IM_LEN (in, 1)) + 1.) / 2.
+ if (IS_INDEFR(GT_YIN(geo)))
+ GT_YIN(geo) = (real (IM_LEN (in, 2)) + 1.) / 2.
+
+ # Determine the final x and y shifts.
+ if (! IS_INDEFR(GT_XSHIFT(geo)))
+ GT_XOUT(geo) = GT_XIN(geo) + GT_XSHIFT(geo)
+ if (! IS_INDEFR(GT_YSHIFT(geo)))
+ GT_YOUT(geo) = GT_YIN(geo) + GT_YSHIFT(geo)
+ GT_XSHIFT(geo) = GT_XIN(geo) - gseval (sx1, GT_XOUT(geo),
+ GT_YOUT(geo))
+ GT_YSHIFT(geo) = GT_YIN(geo) - gseval (sy1, GT_XOUT(geo),
+ GT_YOUT(geo))
+
+ # Alter coefficients.
+ call geo_xyshiftr (sx1, sy1, GT_XSHIFT(geo), GT_YSHIFT(geo))
+end
+
+
+# GEO_DCOEFF -- Alter the linear componets of the surface fit after the fact.
+
+procedure geo_dcoeff (geo, dt, rec, sx1, sy1)
+
+pointer geo #I pointer to geotran structure
+pointer dt #I pointer to database record
+int rec #I database record
+pointer sx1, sy1 #U pointers to the linear surface
+
+real dtgetr()
+errchk dtgetr()
+
+begin
+ # Get the transformation parameters.
+ if (IS_INDEFR(GT_XMAG(geo))) {
+ iferr (GT_XMAG(geo) = dtgetr (dt, rec, "xmag"))
+ GT_XMAG(geo) = dtgetr (dt, rec, "xscale")
+ }
+ if (IS_INDEFR(GT_YMAG(geo))) {
+ iferr (GT_YMAG(geo) = dtgetr (dt, rec, "ymag"))
+ GT_YMAG(geo) = dtgetr (dt, rec, "yscale")
+ }
+ if (IS_INDEFR(GT_XROTATION(geo)))
+ GT_XROTATION(geo) = DEGTORAD(dtgetr (dt, rec, "xrotation"))
+ else
+ GT_XROTATION(geo) = DEGTORAD(GT_XROTATION(geo))
+ if (IS_INDEFR(GT_YROTATION(geo)))
+ GT_YROTATION(geo) = DEGTORAD(dtgetr (dt, rec, "yrotation"))
+ else
+ GT_YROTATION(geo) = DEGTORAD(GT_YROTATION(geo))
+
+ call geo_rotmagr (sx1, sy1, GT_XMAG(geo), GT_YMAG(geo),
+ GT_XROTATION(geo), GT_YROTATION(geo))
+end
+
+
+# GEO_GWCS -- Compute the ltm and ltv vectors using the GEOTRAN coordinate
+# surfaces.
+
+procedure geo_gwcs (geo, sx1, sy1, ltm, ltv)
+
+pointer geo # pointer to the geotran structure
+pointer sx1 # pointer to the linear x coordinate surface
+pointer sy1 # pointer to the linear y coordinate surface
+double ltm[2,2] # rotation matrix
+double ltv[2] # shift vector
+
+double xscale, yscale, xmin, ymin
+int ncoeff
+pointer sp, xcoeff, ycoeff
+real xrange, yrange
+int gsgeti()
+real gsgetr()
+
+begin
+ # Allocate space for the coefficients.
+ call smark (sp)
+ ncoeff = max (gsgeti (sx1, GSNSAVE), gsgeti (sy1, GSNSAVE))
+ call salloc (xcoeff, ncoeff, TY_REAL)
+ call salloc (ycoeff, ncoeff, TY_REAL)
+
+ # Fetch the coefficients.
+ call gssave (sx1, Memr[xcoeff])
+ call gssave (sy1, Memr[ycoeff])
+
+ # Denormalize the coefficients for non-polynomial functions.
+ xrange = gsgetr (sx1, GSXMAX) - gsgetr (sx1, GSXMIN)
+ yrange = gsgetr (sy1, GSYMAX) - gsgetr (sy1, GSYMIN)
+ if (gsgeti (sx1, GSTYPE) != GS_POLYNOMIAL) {
+ Memr[xcoeff+GS_SAVECOEFF+1] = Memr[xcoeff+GS_SAVECOEFF+1] * 2. /
+ xrange
+ Memr[xcoeff+GS_SAVECOEFF+2] = Memr[xcoeff+GS_SAVECOEFF+2] * 2. /
+ yrange
+ }
+ if (gsgeti (sy1, GSTYPE) != GS_POLYNOMIAL) {
+ Memr[ycoeff+GS_SAVECOEFF+1] = Memr[ycoeff+GS_SAVECOEFF+1] * 2. /
+ xrange
+ Memr[ycoeff+GS_SAVECOEFF+2] = Memr[ycoeff+GS_SAVECOEFF+2] * 2. /
+ yrange
+ }
+
+ # Set the shift vector.
+ ltv[1] = Memr[xcoeff+GS_SAVECOEFF]
+ ltv[2] = Memr[ycoeff+GS_SAVECOEFF]
+
+ # Set the rotation vector.
+ ltm[1,1] = Memr[xcoeff+GS_SAVECOEFF+1]
+ ltm[2,1] = Memr[xcoeff+GS_SAVECOEFF+2]
+ ltm[1,2] = Memr[ycoeff+GS_SAVECOEFF+1]
+ ltm[2,2] = Memr[ycoeff+GS_SAVECOEFF+2]
+
+ # Get the sign of the scale vector which is always +ve.
+ xmin = GT_XMIN(geo)
+ ymin = GT_YMIN(geo)
+ if (GT_XMIN(geo) > GT_XMAX(geo))
+ xscale = -GT_XSCALE(geo)
+ else
+ xscale = GT_XSCALE(geo)
+ if (GT_YMIN(geo) > GT_YMAX(geo))
+ yscale = -GT_YSCALE(geo)
+ else
+ yscale = GT_YSCALE(geo)
+
+ # Correct for reference units that are not in pixels.
+ ltv[1] = ltv[1] + ltm[1,1] * xmin + ltm[2,1] * ymin - ltm[1,1] *
+ xscale - ltm[2,1] * yscale
+ ltv[2] = ltv[2] + ltm[1,2] * xmin + ltm[2,2] * ymin - ltm[1,2] *
+ xscale - ltm[2,2] * yscale
+ ltm[1,1] = ltm[1,1] * xscale
+ ltm[2,1] = ltm[2,1] * yscale
+ ltm[1,2] = ltm[1,2] * xscale
+ ltm[2,2] = ltm[2,2] * yscale
+
+ call sfree (sp)
+end
+
+
+define LTM Memd[ltm+(($2)-1)*pdim+($1)-1]
+
+# GEO_SWCS -- Update the wcs and write it to the image header.
+
+procedure geo_swcs (mw, gltm, gltv, ldim)
+
+pointer mw # the mwcs descriptor
+double gltm[ldim,ldim] # the input cd matrix from geotran
+double gltv[ldim] # the input shift vector from geotran
+int ldim # number of logical dimensions
+
+int axes[IM_MAXDIM], naxes, pdim, nelem, axmap, ax1, ax2
+pointer sp, ltm, ltv_1, ltv_2
+int mw_stati()
+
+begin
+ # Convert axis bitflags to the axis lists.
+ if (ldim == 1) {
+ call mw_gaxlist (mw, 01B, axes, naxes)
+ if (naxes < 1)
+ return
+ } else {
+ call mw_gaxlist (mw, 03B, axes, naxes)
+ if (naxes < 2)
+ return
+ }
+
+ # Initialize the parameters.
+ pdim = mw_stati (mw, MW_NDIM)
+ nelem = pdim * pdim
+ axmap = mw_stati (mw, MW_USEAXMAP)
+ call mw_seti (mw, MW_USEAXMAP, NO)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (ltm, nelem, TY_DOUBLE)
+ call salloc (ltv_1, pdim, TY_DOUBLE)
+ call salloc (ltv_2, pdim, TY_DOUBLE)
+
+ # Initialize the vectors and matrices.
+ call mw_mkidmd (Memd[ltm], pdim)
+ call aclrd (Memd[ltv_1], pdim)
+ call aclrd (Memd[ltv_2], pdim)
+
+ # Enter the linear operation.
+ ax1 = axes[1]
+ Memd[ltv_2+ax1-1] = gltv[1]
+ LTM(ax1,ax1) = gltm[1,1]
+ if (ldim == 2) {
+ ax2 = axes[2]
+ Memd[ltv_2+ax2-1] = gltv[2]
+ LTM(ax2,ax1) = gltm[2,1]
+ LTM(ax1,ax2) = gltm[1,2]
+ LTM(ax2,ax2) = gltm[2,2]
+ }
+
+ # Perform the translation.
+ call mw_translated (mw, Memd[ltv_1], Memd[ltm], Memd[ltv_2], pdim)
+
+ call sfree (sp)
+ call mw_seti (mw, MW_USEAXMAP, axmap)
+end
diff --git a/pkg/images/immatch/src/geometry/t_geoxytran.x b/pkg/images/immatch/src/geometry/t_geoxytran.x
new file mode 100644
index 00000000..c99b9a0c
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/t_geoxytran.x
@@ -0,0 +1,343 @@
+include <fset.h>
+include <ctype.h>
+include <math/gsurfit.h>
+
+define MAX_FIELDS 100 # Maximum number of fields in list
+define TABSIZE 8 # Spacing of tab stops
+
+# Define the permitted computation types
+define GEO_REAL 1 # Computation type is real
+define GEO_DOUBLE 2 # Computation type is double
+
+# T_GEOXYTRAN -- Transform a list of x and y coordinates using the geometric
+# transformation operations computed by the GEOMAP task.
+
+procedure t_geoxytran()
+
+int inlist, outlist, reclist, calctype, geometry, dir, xcolumn, ycolumn
+int min_sigdigits, infd, outfd
+pointer sp, in_fname, out_fname, record, xformat, yformat, str, dt
+pointer sx1, sy1, sx2, sy2
+int clgwrd(), clgeti(), open()
+bool streq()
+int fntopnb(), fntlenb(), fntgfnb(), imtopenp(), imtlen(), imtgetim()
+pointer dtmap()
+
+begin
+ # Allocate memory for transformation parameters structure
+ call smark (sp)
+ call salloc (in_fname, SZ_FNAME, TY_CHAR)
+ call salloc (out_fname, SZ_FNAME, TY_CHAR)
+ call salloc (record, SZ_FNAME, TY_CHAR)
+ call salloc (xformat, SZ_FNAME, TY_CHAR)
+ call salloc (yformat, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Open the input and output file lists.
+ call clgstr ("input", Memc[str], SZ_FNAME)
+ if (Memc[str] == EOS)
+ call strcpy ("STDIN", Memc[str], SZ_FNAME)
+ inlist = fntopnb(Memc[str], NO)
+ call clgstr ("output", Memc[str], SZ_FNAME)
+ if (Memc[str] == EOS)
+ call strcpy ("STDOUT", Memc[str], SZ_FNAME)
+ outlist = fntopnb (Memc[str], NO)
+ call clgstr ("database", Memc[str], SZ_FNAME)
+ if (Memc[str] != EOS) {
+ dt = dtmap (Memc[str], READ_ONLY)
+ reclist = imtopenp ("transforms")
+ } else {
+ dt = NULL
+ reclist = NULL
+ }
+
+ # Test the input and out file and record lists for validity.
+ if (fntlenb(inlist) <= 0)
+ call error (0, "The input file list is empty")
+ if (fntlenb(outlist) <= 0)
+ call error (0, "The output file list is empty")
+ if (fntlenb(outlist) > 1 && fntlenb(outlist) != fntlenb(inlist))
+ call error (0,
+ "Input and output file lists are not the same length")
+ if (dt != NULL && reclist != NULL) {
+ if (imtlen (reclist) > 1 && imtlen (reclist) != fntlenb (inlist))
+ call error (0,
+ "Input file and record lists are not the same length.")
+ }
+
+ # Get geometry and transformation direction.
+ geometry = clgwrd ("geometry", Memc[str], SZ_LINE,
+ ",linear,distortion,geometric,")
+ dir = clgwrd ("direction", Memc[str], SZ_LINE,
+ ",forward,backward,")
+
+ # Get field numbers from cl
+ if (dir == 1)
+ calctype = clgwrd ("calctype", Memc[str], SZ_LINE,
+ ",real,double,")
+ else
+ calctype = GEO_DOUBLE
+ xcolumn = clgeti ("xcolumn")
+ ycolumn = clgeti ("ycolumn")
+ call clgstr ("xformat", Memc[xformat], SZ_FNAME)
+ call clgstr ("yformat", Memc[yformat], SZ_FNAME)
+ min_sigdigits = clgeti ("min_sigdigits")
+
+ # Get the output file name.
+ if (fntgfnb (outlist, Memc[out_fname], SZ_FNAME) == EOF)
+ call strcpy ("STDOUT", Memc[out_fname], SZ_FNAME)
+ outfd = open (Memc[out_fname], NEW_FILE, TEXT_FILE)
+ if (streq (Memc[out_fname], "STDOUT") || outfd == STDOUT)
+ call fseti (outfd, F_FLUSHNL, YES)
+
+ # Get the record name.
+ if (reclist == NULL)
+ Memc[record] = EOS
+ else if (imtgetim (reclist, Memc[record], SZ_FNAME) == EOF)
+ Memc[record] = EOS
+
+ # Call procedure to get parameters and fill structure.
+ sx1 = NULL; sy1 = NULL; sx2 = NULL; sy2 = NULL
+ call geo_init_transform (dt, Memc[record], calctype, geometry,
+ sx1, sy1, sx2, sy2)
+
+ # While input list is not depleted, open file and transform list.
+ while (fntgfnb (inlist, Memc[in_fname], SZ_FNAME) != EOF) {
+
+ infd = open (Memc[in_fname], READ_ONLY, TEXT_FILE)
+
+ # Transform the coordinates.
+ call geo_transform_file (infd, outfd, xcolumn, ycolumn, dir,
+ calctype, Memc[xformat], Memc[yformat], min_sigdigits,
+ sx1, sy1, sx2, sy2)
+
+ # Do not get a new output file name if there is not output
+ # file list or if only one output file was specified.
+ # Otherwise fetch the new name.
+ if (fntlenb(outlist) > 1) {
+ call close (outfd)
+ if (fntgfnb (outlist, Memc[out_fname], SZ_FNAME) != EOF)
+ outfd = open (Memc[out_fname], NEW_FILE, TEXT_FILE)
+ if (streq (Memc[out_fname], "STDOUT") || outfd == STDOUT)
+ call fseti (outfd, F_FLUSHNL, YES)
+ }
+
+ call close (infd)
+
+ # Do not reset the transformation if there is no record list
+ # or only one record is specified. Otherwise fetch the next
+ # record name.
+ if (reclist != NULL && imtlen (reclist) > 1) {
+ if (imtgetim (reclist, Memc[record], SZ_FNAME) != EOF) {
+ call geo_free_transform (calctype, sx1, sy1, sx2, sy2)
+ call geo_init_transform (dt, Memc[record], calctype,
+ geometry, sx1, sy1, sx2, sy2)
+ }
+ }
+ }
+
+ # Free the surface descriptors.
+ call geo_free_transform (calctype, sx1, sy1, sx2, sy2)
+
+ # Close up file and record templates.
+ if (dt != NULL)
+ call dtunmap (dt)
+ call close (outfd)
+ call fntclsb (inlist)
+ call fntclsb (outlist)
+ if (reclist != NULL)
+ call imtclose (reclist)
+ call sfree (sp)
+end
+
+
+# GEO_INIT_TRANSFORM -- gets parameter values relevant to the
+# transformation from the cl. List entries will be transformed
+# in procedure rg_transform.
+
+procedure geo_init_transform (dt, record, calctype, geometry, sx1, sy1,
+ sx2, sy2)
+
+pointer dt #I pointer to database file produced by geomap
+char record[ARB] #I the name of the database record
+int calctype #I the computation data type
+int geometry #I the type of geometry to be computed
+pointer sx1, sy1 #O pointers to the linear x and y surfaces
+pointer sx2, sy2 #O pointers to the x and y distortion surfaces
+
+begin
+ if (dt == NULL) {
+
+ if (calctype == GEO_REAL)
+ call geo_linitr (sx1, sy1, sx2, sy2)
+ else
+ call geo_linitd (sx1, sy1, sx2, sy2)
+
+ } else {
+
+ if (calctype == GEO_REAL)
+ call geo_sinitr (dt, record, geometry, sx1, sy1,
+ sx2, sy2)
+ else
+ call geo_sinitd (dt, record, geometry, sx1, sy1,
+ sx2, sy2)
+ }
+end
+
+
+# GEO_FREE_TRANSFORM -- Free the previously defined transformation
+
+procedure geo_free_transform (calctype, sx1, sy1, sx2, sy2)
+
+int calctype #I the computation data type
+pointer sx1, sy1 #O pointers to the linear x and y surfaces
+pointer sx2, sy2 #O pointers to the x and y distortion surfaces
+
+begin
+ if (calctype == GEO_REAL)
+ call geo_sfreer (sx1, sy1, sx2, sy2)
+ else
+ call geo_sfreed (sx1, sy1, sx2, sy2)
+end
+
+
+# GEO_TRANSFORM_FILE -- This procedure is called once for each file
+# in the input list. For each line in the input file that isn't
+# blank or comment, the line is transformed. Blank and comment
+# lines are output unaltered.
+
+procedure geo_transform_file (infd, outfd, xfield, yfield, dir, calctype,
+ xformat, yformat, min_sigdigits, sx1, sy1, sx2, sy2)
+
+int infd #I the input file descriptor
+int outfd #I the output file descriptor
+int xfield #I the x column number
+int yfield #I the y column number
+int dir #I transform direction
+int calctype #I the computation type
+char xformat[ARB] #I output format of the x coordinate
+char yformat[ARB] #I output format of the y coordinate
+int min_sigdigits #I the minimum number of digits to be output
+pointer sx1, sy1 #I pointers to the linear x and y surfaces
+pointer sx2, sy2 #I pointers to the x and y distortion surfaces
+
+double xd, yd, xtd, ytd
+int max_fields, nline, nfields, nchars, nsdig_x, nsdig_y, offset
+real xr, yr, xtr, ytr
+pointer sp, inbuf, linebuf, field_pos, outbuf, ip
+int getline(), li_get_numr(), li_get_numd()
+
+int nsx, nsy
+double der[8], xmin, xmax, ymin, ymax, tol
+pointer sx[2], sy[2]
+double dgsgetd()
+
+#double x, y, xt, yt
+
+begin
+ call smark (sp)
+ call salloc (inbuf, SZ_LINE, TY_CHAR)
+ call salloc (linebuf, SZ_LINE, TY_CHAR)
+ call salloc (field_pos, MAX_FIELDS, TY_INT)
+ call salloc (outbuf, SZ_LINE, TY_CHAR)
+
+ max_fields = MAX_FIELDS
+
+ # Initialize for backward transform.
+ if (dir == 2) {
+ sx[1] = sx1; sy[1] = sy1; sx[2] = sx2; sy[2] = sy2
+ nsx = 2; nsy = 2
+ if (sx2 == NULL)
+ nsx = 1
+ if (sy2 == NULL)
+ nsy = 1
+ xmin = dgsgetd (sx1, GSXMIN)
+ xmax = dgsgetd (sx1, GSXMAX)
+ ymin = dgsgetd (sx1, GSYMIN)
+ ymax = dgsgetd (sx1, GSYMAX)
+ tol = abs (xmax - xmin) / 1E10
+ xd = (xmin + xmax) / 2
+ yd = (ymin + ymax) / 2
+ call tr_init (sx, nsx, sy, nsy, xd, yd, der)
+ }
+
+ for (nline=1; getline (infd, Memc[inbuf]) != EOF; nline = nline + 1) {
+ for (ip=inbuf; IS_WHITE(Memc[ip]); ip=ip+1)
+ ;
+ if (Memc[ip] == '#') {
+ # Pass comment lines on to the output unchanged.
+ call putline (outfd, Memc[inbuf])
+ next
+ } else if (Memc[ip] == '\n' || Memc[ip] == EOS) {
+ # Blank lines too.
+ call putline (outfd, Memc[inbuf])
+ next
+ }
+
+ # Expand tabs into blanks, determine field offsets.
+ call strdetab (Memc[inbuf], Memc[linebuf], SZ_LINE, TABSIZE)
+ call li_find_fields (Memc[linebuf], Memi[field_pos], max_fields,
+ nfields)
+
+ if (xfield > nfields || yfield > nfields) {
+ call fstats (infd, F_FILENAME, Memc[outbuf], SZ_LINE)
+ call eprintf ("Not enough fields in file %s line %d\n")
+ call pargstr (Memc[outbuf])
+ call pargi (nline)
+ call putline (outfd, Memc[linebuf])
+ next
+ }
+
+ offset = Memi[field_pos+xfield-1]
+ if (calctype == GEO_REAL)
+ nchars = li_get_numr (Memc[linebuf+offset-1], xr, nsdig_x)
+ else
+ nchars = li_get_numd (Memc[linebuf+offset-1], xd, nsdig_x)
+ if (nchars == 0) {
+ call fstats (infd, F_FILENAME, Memc[outbuf], SZ_LINE)
+ call eprintf ("Bad x value in file '%s' at line %d:\n")
+ call pargstr (Memc[outbuf])
+ call pargi (nline)
+ call putline (outfd, Memc[linebuf])
+ next
+ }
+
+ offset = Memi[field_pos+yfield-1]
+ if (calctype == GEO_REAL)
+ nchars = li_get_numr (Memc[linebuf+offset-1], yr, nsdig_y)
+ else
+ nchars = li_get_numd (Memc[linebuf+offset-1], yd, nsdig_y)
+ if (nchars == 0) {
+ call fstats (infd, F_FILENAME, Memc[outbuf], SZ_LINE)
+ call eprintf ("Bad y value in file '%s' at line %d:\n")
+ call pargstr (Memc[outbuf])
+ call pargi (nline)
+ call putline (outfd, Memc[linebuf])
+ next
+ }
+
+ if (calctype == GEO_REAL) {
+ call geo_do_transformr (xr, yr, xtr, ytr,
+ sx1, sy1, sx2, sy2)
+ call li_pack_liner (Memc[linebuf], Memc[outbuf], SZ_LINE,
+ Memi[field_pos], nfields, xfield, yfield, xtr, ytr,
+ xformat, yformat, nsdig_x, nsdig_y, min_sigdigits)
+
+ } else {
+ if (dir == 1)
+ call geo_do_transformd (xd, yd, xtd, ytd,
+ sx1, sy1, sx2, sy2)
+ else
+ call tr_invert (sx, nsx, sy, nsy, xd, yd, xtd, ytd,
+ der, xmin, xmax, ymin, ymax, tol)
+ call li_pack_lined (Memc[linebuf], Memc[outbuf], SZ_LINE,
+ Memi[field_pos], nfields, xfield, yfield, xtd, ytd,
+ xformat, yformat, nsdig_x, nsdig_y, min_sigdigits)
+ }
+
+ call putline (outfd, Memc[outbuf])
+ }
+
+ call sfree (sp)
+end
+
diff --git a/pkg/images/immatch/src/geometry/trinvert.x b/pkg/images/immatch/src/geometry/trinvert.x
new file mode 100644
index 00000000..5f75cdc2
--- /dev/null
+++ b/pkg/images/immatch/src/geometry/trinvert.x
@@ -0,0 +1,163 @@
+# The code here is taken from t_transform.x in the longslit package. The
+# changes are to use a sum instead of an average when multiple surfaces
+# are given and not to use the xgs interface. Also the convergence
+# tolerance is user specified since in this application the units might
+# not be pixels.
+
+
+define MAX_ITERATE 20
+define ERROR 0.05
+define FUDGE 0.5
+
+# TR_INVERT -- Given user coordinate surfaces U(X,Y) and V(X,Y)
+# (if none use one-to-one mapping and if more than one sum)
+# corresponding to a given U and V and also the various partial
+# derivatives. This is done using a gradient following interative
+# method based on evaluating the partial derivative at each point
+# and solving the linear Taylor expansions simultaneously. The last
+# point sampled is used as the starting point. Thus, if the
+# input U and V progress smoothly then the number of iterations
+# can be small. The output is returned in x and y and in the derivative array
+# DER. A point outside of the surfaces is returned as the nearest
+# point at the edge of the surfaces in the DER array.
+
+procedure tr_invert (usf, nusf, vsf, nvsf, u, v, x, y, der,
+ xmin, xmax, ymin, ymax, tol)
+
+pointer usf[ARB], vsf[ARB] # User coordinate surfaces U(X,Y) and V(X,Y)
+int nusf, nvsf # Number of surfaces for each coordinate
+double u, v # Input U and V to determine X and Y
+double x, y # Output X and Y
+double der[8] # Last result as input, new result as output
+ # 1=X, 2=Y, 3=U, 4=DUDX, 5=DUDY, 6=V,
+ # 7=DVDX, 8=DVDY
+double xmin, xmax, ymin, ymax # Limits of coordinate surfaces.
+double tol # Tolerance
+
+int i, j, nedge
+double fudge, du, dv, dx, dy, tmp[3]
+
+begin
+ # Use the last result as the starting point for the next position.
+ # If this is near the desired value then the interation will converge
+ # quickly. Allow a iteration to go off the surface twice.
+ # Quit when DX and DY are within tol.
+
+ nedge = 0
+ do i = 1, MAX_ITERATE {
+ du = u - der[3]
+ dv = v - der[6]
+ dx = (der[8] * du - der[5] * dv) /
+ (der[8] * der[4] - der[5] * der[7])
+ dy = (dv - der[7] * dx) / der[8]
+ fudge = 1 - FUDGE / i
+ x = der[1] + fudge * dx
+ y = der[2] + fudge * dy
+ der[1] = max (xmin, min (xmax, x))
+ der[2] = max (ymin, min (ymax, y))
+ if ((abs (dx) < tol) && (abs (dy) < tol))
+ break
+
+ if (nusf == 0)
+ der[3] = der[1]
+ else if (nusf == 1) {
+ call dgsder (usf[1], der[1], der[2], der[3], 1, 0, 0)
+ call dgsder (usf[1], der[1], der[2], der[4], 1, 1, 0)
+ call dgsder (usf[1], der[1], der[2], der[5], 1, 0, 1)
+ } else {
+ call dgsder (usf[1], der[1], der[2], der[3], 1, 0, 0)
+ call dgsder (usf[1], der[1], der[2], der[4], 1, 1, 0)
+ call dgsder (usf[1], der[1], der[2], der[5], 1, 0, 1)
+ do j = 2, nusf {
+ call dgsder (usf[j], der[1], der[2], tmp[1], 1, 0, 0)
+ call dgsder (usf[j], der[1], der[2], tmp[2], 1, 1, 0)
+ call dgsder (usf[j], der[1], der[2], tmp[3], 1, 0, 1)
+ der[3] = der[3] + tmp[1]
+ der[4] = der[4] + tmp[2]
+ der[5] = der[5] + tmp[3]
+ }
+ }
+
+ if (nvsf == 0)
+ der[6] = der[2]
+ else if (nvsf == 1) {
+ call dgsder (vsf[1], der[1], der[2], der[6], 1, 0, 0)
+ call dgsder (vsf[1], der[1], der[2], der[7], 1, 1, 0)
+ call dgsder (vsf[1], der[1], der[2], der[8], 1, 0, 1)
+ } else {
+ call dgsder (vsf[1], der[1], der[2], der[6], 1, 0, 0)
+ call dgsder (vsf[1], der[1], der[2], der[7], 1, 1, 0)
+ call dgsder (vsf[1], der[1], der[2], der[8], 1, 0, 1)
+ do j = 2, nvsf {
+ call dgsder (vsf[j], der[1], der[2], tmp[1], 1, 0, 0)
+ call dgsder (vsf[j], der[1], der[2], tmp[2], 1, 1, 0)
+ call dgsder (vsf[j], der[1], der[2], tmp[3], 1, 0, 1)
+ der[6] = der[6] + tmp[1]
+ der[7] = der[7] + tmp[2]
+ der[8] = der[8] + tmp[3]
+ }
+ }
+ }
+end
+
+
+# TR_INIT -- Since the inversion iteration always begins from the last
+# point we need to initialize before the first call to TR_INVERT.
+
+procedure tr_init (usf, nusf, vsf, nvsf, x, y, der)
+
+pointer usf[ARB], vsf[ARB] # User coordinate surfaces
+int nusf, nvsf # Number of surfaces for each coordinate
+double x, y # Starting X and Y
+double der[8] # Inversion data
+
+int j
+double tmp[3]
+
+begin
+ der[1] = x
+ der[2] = y
+ if (nusf == 0) {
+ der[3] = der[1]
+ der[4] = 1.
+ der[5] = 0.
+ } else if (nusf == 1) {
+ call dgsder (usf[1], der[1], der[2], der[3], 1, 0, 0)
+ call dgsder (usf[1], der[1], der[2], der[4], 1, 1, 0)
+ call dgsder (usf[1], der[1], der[2], der[5], 1, 0, 1)
+ } else {
+ call dgsder (usf[1], der[1], der[2], der[3], 1, 0, 0)
+ call dgsder (usf[1], der[1], der[2], der[4], 1, 1, 0)
+ call dgsder (usf[1], der[1], der[2], der[5], 1, 0, 1)
+ do j = 2, nusf {
+ call dgsder (usf[j], der[1], der[2], tmp[1], 1, 0, 0)
+ call dgsder (usf[j], der[1], der[2], tmp[2], 1, 1, 0)
+ call dgsder (usf[j], der[1], der[2], tmp[3], 1, 0, 1)
+ der[3] = der[3] + tmp[1]
+ der[4] = der[4] + tmp[2]
+ der[5] = der[5] + tmp[3]
+ }
+ }
+
+ if (nvsf == 0) {
+ der[6] = der[2]
+ der[7] = 0.
+ der[8] = 1.
+ } else if (nvsf == 1) {
+ call dgsder (vsf[1], der[1], der[2], der[6], 1, 0, 0)
+ call dgsder (vsf[1], der[1], der[2], der[7], 1, 1, 0)
+ call dgsder (vsf[1], der[1], der[2], der[8], 1, 0, 1)
+ } else {
+ call dgsder (vsf[1], der[1], der[2], der[6], 1, 0, 0)
+ call dgsder (vsf[1], der[1], der[2], der[7], 1, 1, 0)
+ call dgsder (vsf[1], der[1], der[2], der[8], 1, 0, 1)
+ do j = 2, nvsf {
+ call dgsder (vsf[j], der[1], der[2], tmp[1], 1, 0, 0)
+ call dgsder (vsf[j], der[1], der[2], tmp[2], 1, 1, 0)
+ call dgsder (vsf[j], der[1], der[2], tmp[3], 1, 0, 1)
+ der[6] = der[6] + tmp[1]
+ der[7] = der[7] + tmp[2]
+ der[8] = der[8] + tmp[3]
+ }
+ }
+end
diff --git a/pkg/images/immatch/src/imcombine/imcombine.par b/pkg/images/immatch/src/imcombine/imcombine.par
new file mode 100644
index 00000000..ead908e4
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/imcombine.par
@@ -0,0 +1,43 @@
+# IMCOMBINE -- Image combine parameters
+
+input,s,a,,,,List of images to combine
+output,s,a,,,,List of output images
+headers,s,h,"",,,List of header files (optional)
+bpmasks,s,h,"",,,List of bad pixel masks (optional)
+rejmasks,s,h,"",,,List of rejection masks (optional)
+nrejmasks,s,h,"",,,List of number rejected masks (optional)
+expmasks,s,h,"",,,List of exposure masks (optional)
+sigmas,s,h,"",,,List of sigma images (optional)
+imcmb,s,h,"$I",,,Keyword for IMCMB keywords
+logfile,s,h,"STDOUT",,,"Log file
+"
+combine,s,h,"average","average|median|lmedian|sum|quadrature|nmodel",,Type of combine operation
+reject,s,h,"none","none|minmax|ccdclip|crreject|sigclip|avsigclip|pclip",,Type of rejection
+project,b,h,no,,,Project highest dimension of input images?
+outtype,s,h,"real","short|ushort|integer|long|real|double",,Output image pixel datatype
+outlimits,s,h,"",,,Output limits (x1 x2 y1 y2 ...)
+offsets,f,h,"none",,,Input image offsets
+masktype,s,h,"none","",,Mask type
+maskvalue,s,h,"0",,,Mask value
+blank,r,h,0.,,,"Value if there are no pixels
+"
+scale,s,h,"none",,,Image scaling
+zero,s,h,"none",,,Image zero point offset
+weight,s,h,"none",,,Image weights
+statsec,s,h,"",,,Image section for computing statistics
+expname,s,h,"",,,"Image header exposure time keyword
+"
+lthreshold,r,h,INDEF,,,Lower threshold
+hthreshold,r,h,INDEF,,,Upper threshold
+nlow,i,h,1,0,,minmax: Number of low pixels to reject
+nhigh,i,h,1,0,,minmax: Number of high pixels to reject
+nkeep,i,h,1,,,Minimum to keep (pos) or maximum to reject (neg)
+mclip,b,h,yes,,,Use median in sigma clipping algorithms?
+lsigma,r,h,3.,0.,,Lower sigma clipping factor
+hsigma,r,h,3.,0.,,Upper sigma clipping factor
+rdnoise,s,h,"0.",,,ccdclip: CCD readout noise (electrons)
+gain,s,h,"1.",,,ccdclip: CCD gain (electrons/DN)
+snoise,s,h,"0.",,,ccdclip: Sensitivity noise (fraction)
+sigscale,r,h,0.1,0.,,Tolerance for sigma clipping scaling corrections
+pclip,r,h,-0.5,,,pclip: Percentile clipping parameter
+grow,r,h,0.,0.,,Radius (pixels) for neighbor rejection
diff --git a/pkg/images/immatch/src/imcombine/mkpkg b/pkg/images/immatch/src/imcombine/mkpkg
new file mode 100644
index 00000000..456232e8
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/mkpkg
@@ -0,0 +1,20 @@
+# Make the IMCOMBINE Task.
+
+$checkout libpkg.a ../../../
+$update libpkg.a
+$checkin libpkg.a ../../../
+$exit
+
+standalone:
+ $set LIBS1 = "src/libimc.a -lxtools -lcurfit -lsurfit -lgsurfit"
+ $set LIBS2 = "-liminterp -lnlfit -lslalib -lncar -lgks"
+ $update libimc.a@src
+ $update libpkg.a
+ $omake x_imcombine.x
+ $link x_imcombine.o libpkg.a $(LIBS1) $(LIBS2) -o xx_imcombine.e
+ ;
+
+libpkg.a:
+ t_imcombine.x src/icombine.com src/icombine.h <error.h> <mach.h> \
+ <imhdr.h>
+ ;
diff --git a/pkg/images/immatch/src/imcombine/src/Revisions b/pkg/images/immatch/src/imcombine/src/Revisions
new file mode 100644
index 00000000..469f9e5c
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/Revisions
@@ -0,0 +1,36 @@
+.help revisions Jul04 imcombine/src
+.nf
+
+This directory contains generic code used in various tasks that combine
+images.
+
+=======
+V2.13
+=======
+
+icgdata.gx
+ Fixed a problem where 3-D images were closing an image in the case
+ of many bands leading to a slow execution (10/20/06, Valdes)
+
+=======
+V2.12.3
+=======
+
+icmask.x
+iclog.x
+icombine.h
+ As a special unadvertised feature the "maskvalue" parameter may be
+ specified with a leading '<' or '>'. Ultimately a full expression
+ should be added and documented. (7/26/04, Valdes)
+
+icmask.x
+ Added a feature to allow masks specified without a path to be found
+ either in the current directory or the directory with the image. This
+ is useful when images to be combined are distributed across multiple
+ directories. (7/16/04, Valdes)
+
+========
+V2.12.2a
+========
+
+.endhelp
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icaclip.x b/pkg/images/immatch/src/imcombine/src/generic/icaclip.x
new file mode 100644
index 00000000..8fb89b1b
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icaclip.x
@@ -0,0 +1,2207 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 3 # Minimum number of images for this algorithm
+
+
+# IC_AAVSIGCLIP -- Reject pixels using an average sigma about the average
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_aavsigclips (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, s1, r, one
+data one /1.0/
+pointer sp, sums, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (sums, npts, TY_REAL)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Since the unweighted average is computed here possibly skip combining
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Compute the unweighted average with the high and low rejected and
+ # the poisson scaled average sigma. There must be at least three
+ # pixels at each point to define the average and contributions to
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ nin = max (0, n[1])
+ s = 0.
+ n2 = 0
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3)
+ next
+
+ # Unweighted average with the high and low rejected
+ low = Mems[d[1]+k]
+ high = Mems[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Mems[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Mems[dp1]
+ l = Memi[mp1]
+ s1 = max (one, (a + zeros[l]) / scales[l])
+ s = s + (d1 - a) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, a)
+ do j = 1, n1
+ s = s + (Mems[d[j]+k] - a) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the average and sum for later.
+ average[i] = a
+ Memr[sums+k] = sum
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ s = sqrt (s / (n2 - 1))
+
+ # Reject pixels and compute the final average (if needed).
+ # There must be at least three pixels at each point for rejection.
+ # Iteratively scale the mean sigma and reject pixels
+ # Compact the data and keep track of the image IDs if needed.
+
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (2, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Mems[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mems[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ a = average[i]
+ sum = Memr[sums+k]
+
+ repeat {
+ n2 = n1
+ if (s > 0.) {
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Mems[dp1]
+ l = Memi[mp1]
+ s1 = s * sqrt (max (one, (a+zeros[l]) / scales[l]))
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ s1 = s * sqrt (max (one, a))
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Mems[dp1]
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mems[dp1]
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Mems[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mems[dp1]
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Mems[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MAVSIGCLIP -- Reject pixels using an average sigma about the median
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_mavsigclips (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+pointer sp, resid, mp1, mp2
+real med, low, high, sig, r, s, s1, one
+data one /1.0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute the poisson scaled average sigma about the median.
+ # There must be at least three pixels at each point to define
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ s = 0.
+ n2 = 0
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3) {
+ if (n1 == 0)
+ median[i] = blank
+ else if (n1 == 1)
+ median[i] = Mems[d[1]+k]
+ else {
+ low = Mems[d[1]+k]
+ high = Mems[d[2]+k]
+ median[i] = (low + high) / 2.
+ }
+ next
+ }
+
+ # Median
+ n3 = 1 + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Mems[d[n3-1]+k]
+ high = Mems[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Mems[d[n3]+k]
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ l = Memi[m[j]+k]
+ s1 = max (one, (med + zeros[l]) / scales[l])
+ s = s + (Mems[d[j]+k] - med) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, med)
+ do j = 1, n1
+ s = s + (Mems[d[j]+k] - med) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the median for later.
+ median[i] = med
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ sig = sqrt (s / (n2 - 1))
+ else {
+ call sfree (sp)
+ return
+ }
+
+ # Compute individual sigmas and iteratively clip.
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 < max (3, maxkeep+1))
+ next
+ nl = 1
+ nh = n1
+ med = median[i]
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 >= max (MINCLIP, maxkeep+1) && sig > 0.) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (med - Mems[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (Mems[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ s1 = sig * sqrt (max (one, med))
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Mems[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Mems[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Mems[d[n3-1]+k]
+ high = Mems[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Mems[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Mems[d[n3-1]+k]
+ high = Mems[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Mems[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mems[d[l]+k] = Mems[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mems[d[l]+k] = Mems[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_AAVSIGCLIP -- Reject pixels using an average sigma about the average
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_aavsigclipi (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, s1, r, one
+data one /1.0/
+pointer sp, sums, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (sums, npts, TY_REAL)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Since the unweighted average is computed here possibly skip combining
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Compute the unweighted average with the high and low rejected and
+ # the poisson scaled average sigma. There must be at least three
+ # pixels at each point to define the average and contributions to
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ nin = max (0, n[1])
+ s = 0.
+ n2 = 0
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3)
+ next
+
+ # Unweighted average with the high and low rejected
+ low = Memi[d[1]+k]
+ high = Memi[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memi[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Memi[dp1]
+ l = Memi[mp1]
+ s1 = max (one, (a + zeros[l]) / scales[l])
+ s = s + (d1 - a) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, a)
+ do j = 1, n1
+ s = s + (Memi[d[j]+k] - a) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the average and sum for later.
+ average[i] = a
+ Memr[sums+k] = sum
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ s = sqrt (s / (n2 - 1))
+
+ # Reject pixels and compute the final average (if needed).
+ # There must be at least three pixels at each point for rejection.
+ # Iteratively scale the mean sigma and reject pixels
+ # Compact the data and keep track of the image IDs if needed.
+
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (2, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memi[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memi[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ a = average[i]
+ sum = Memr[sums+k]
+
+ repeat {
+ n2 = n1
+ if (s > 0.) {
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Memi[dp1]
+ l = Memi[mp1]
+ s1 = s * sqrt (max (one, (a+zeros[l]) / scales[l]))
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ s1 = s * sqrt (max (one, a))
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Memi[dp1]
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memi[dp1]
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memi[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memi[dp1]
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memi[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MAVSIGCLIP -- Reject pixels using an average sigma about the median
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_mavsigclipi (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+pointer sp, resid, mp1, mp2
+real med, low, high, sig, r, s, s1, one
+data one /1.0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute the poisson scaled average sigma about the median.
+ # There must be at least three pixels at each point to define
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ s = 0.
+ n2 = 0
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3) {
+ if (n1 == 0)
+ median[i] = blank
+ else if (n1 == 1)
+ median[i] = Memi[d[1]+k]
+ else {
+ low = Memi[d[1]+k]
+ high = Memi[d[2]+k]
+ median[i] = (low + high) / 2.
+ }
+ next
+ }
+
+ # Median
+ n3 = 1 + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memi[d[n3-1]+k]
+ high = Memi[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memi[d[n3]+k]
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ l = Memi[m[j]+k]
+ s1 = max (one, (med + zeros[l]) / scales[l])
+ s = s + (Memi[d[j]+k] - med) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, med)
+ do j = 1, n1
+ s = s + (Memi[d[j]+k] - med) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the median for later.
+ median[i] = med
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ sig = sqrt (s / (n2 - 1))
+ else {
+ call sfree (sp)
+ return
+ }
+
+ # Compute individual sigmas and iteratively clip.
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 < max (3, maxkeep+1))
+ next
+ nl = 1
+ nh = n1
+ med = median[i]
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 >= max (MINCLIP, maxkeep+1) && sig > 0.) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (med - Memi[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (Memi[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ s1 = sig * sqrt (max (one, med))
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memi[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memi[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memi[d[n3-1]+k]
+ high = Memi[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memi[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memi[d[n3-1]+k]
+ high = Memi[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memi[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memi[d[l]+k] = Memi[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memi[d[l]+k] = Memi[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_AAVSIGCLIP -- Reject pixels using an average sigma about the average
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_aavsigclipr (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, s1, r, one
+data one /1.0/
+pointer sp, sums, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (sums, npts, TY_REAL)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Since the unweighted average is computed here possibly skip combining
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Compute the unweighted average with the high and low rejected and
+ # the poisson scaled average sigma. There must be at least three
+ # pixels at each point to define the average and contributions to
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ nin = max (0, n[1])
+ s = 0.
+ n2 = 0
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3)
+ next
+
+ # Unweighted average with the high and low rejected
+ low = Memr[d[1]+k]
+ high = Memr[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memr[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Memr[dp1]
+ l = Memi[mp1]
+ s1 = max (one, (a + zeros[l]) / scales[l])
+ s = s + (d1 - a) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, a)
+ do j = 1, n1
+ s = s + (Memr[d[j]+k] - a) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the average and sum for later.
+ average[i] = a
+ Memr[sums+k] = sum
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ s = sqrt (s / (n2 - 1))
+
+ # Reject pixels and compute the final average (if needed).
+ # There must be at least three pixels at each point for rejection.
+ # Iteratively scale the mean sigma and reject pixels
+ # Compact the data and keep track of the image IDs if needed.
+
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (2, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memr[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memr[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ a = average[i]
+ sum = Memr[sums+k]
+
+ repeat {
+ n2 = n1
+ if (s > 0.) {
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Memr[dp1]
+ l = Memi[mp1]
+ s1 = s * sqrt (max (one, (a+zeros[l]) / scales[l]))
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ s1 = s * sqrt (max (one, a))
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Memr[dp1]
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memr[dp1]
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memr[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memr[dp1]
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memr[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MAVSIGCLIP -- Reject pixels using an average sigma about the median
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_mavsigclipr (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+pointer sp, resid, mp1, mp2
+real med, low, high, sig, r, s, s1, one
+data one /1.0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute the poisson scaled average sigma about the median.
+ # There must be at least three pixels at each point to define
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ s = 0.
+ n2 = 0
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3) {
+ if (n1 == 0)
+ median[i] = blank
+ else if (n1 == 1)
+ median[i] = Memr[d[1]+k]
+ else {
+ low = Memr[d[1]+k]
+ high = Memr[d[2]+k]
+ median[i] = (low + high) / 2.
+ }
+ next
+ }
+
+ # Median
+ n3 = 1 + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memr[d[n3-1]+k]
+ high = Memr[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memr[d[n3]+k]
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ l = Memi[m[j]+k]
+ s1 = max (one, (med + zeros[l]) / scales[l])
+ s = s + (Memr[d[j]+k] - med) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, med)
+ do j = 1, n1
+ s = s + (Memr[d[j]+k] - med) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the median for later.
+ median[i] = med
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ sig = sqrt (s / (n2 - 1))
+ else {
+ call sfree (sp)
+ return
+ }
+
+ # Compute individual sigmas and iteratively clip.
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 < max (3, maxkeep+1))
+ next
+ nl = 1
+ nh = n1
+ med = median[i]
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 >= max (MINCLIP, maxkeep+1) && sig > 0.) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (med - Memr[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (Memr[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ s1 = sig * sqrt (max (one, med))
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memr[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memr[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memr[d[n3-1]+k]
+ high = Memr[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memr[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memr[d[n3-1]+k]
+ high = Memr[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memr[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memr[d[l]+k] = Memr[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memr[d[l]+k] = Memr[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_AAVSIGCLIP -- Reject pixels using an average sigma about the average
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_aavsigclipd (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+double average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+double d1, low, high, sum, a, s, s1, r, one
+data one /1.0D0/
+pointer sp, sums, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (sums, npts, TY_REAL)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Since the unweighted average is computed here possibly skip combining
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Compute the unweighted average with the high and low rejected and
+ # the poisson scaled average sigma. There must be at least three
+ # pixels at each point to define the average and contributions to
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ nin = max (0, n[1])
+ s = 0.
+ n2 = 0
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3)
+ next
+
+ # Unweighted average with the high and low rejected
+ low = Memd[d[1]+k]
+ high = Memd[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memd[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Memd[dp1]
+ l = Memi[mp1]
+ s1 = max (one, (a + zeros[l]) / scales[l])
+ s = s + (d1 - a) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, a)
+ do j = 1, n1
+ s = s + (Memd[d[j]+k] - a) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the average and sum for later.
+ average[i] = a
+ Memr[sums+k] = sum
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ s = sqrt (s / (n2 - 1))
+
+ # Reject pixels and compute the final average (if needed).
+ # There must be at least three pixels at each point for rejection.
+ # Iteratively scale the mean sigma and reject pixels
+ # Compact the data and keep track of the image IDs if needed.
+
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (2, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memd[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memd[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ a = average[i]
+ sum = Memr[sums+k]
+
+ repeat {
+ n2 = n1
+ if (s > 0.) {
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Memd[dp1]
+ l = Memi[mp1]
+ s1 = s * sqrt (max (one, (a+zeros[l]) / scales[l]))
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ s1 = s * sqrt (max (one, a))
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Memd[dp1]
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memd[dp1]
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memd[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memd[dp1]
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memd[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MAVSIGCLIP -- Reject pixels using an average sigma about the median
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_mavsigclipd (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+double median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+pointer sp, resid, mp1, mp2
+double med, low, high, sig, r, s, s1, one
+data one /1.0D0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute the poisson scaled average sigma about the median.
+ # There must be at least three pixels at each point to define
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ s = 0.
+ n2 = 0
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3) {
+ if (n1 == 0)
+ median[i] = blank
+ else if (n1 == 1)
+ median[i] = Memd[d[1]+k]
+ else {
+ low = Memd[d[1]+k]
+ high = Memd[d[2]+k]
+ median[i] = (low + high) / 2.
+ }
+ next
+ }
+
+ # Median
+ n3 = 1 + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memd[d[n3-1]+k]
+ high = Memd[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memd[d[n3]+k]
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ l = Memi[m[j]+k]
+ s1 = max (one, (med + zeros[l]) / scales[l])
+ s = s + (Memd[d[j]+k] - med) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, med)
+ do j = 1, n1
+ s = s + (Memd[d[j]+k] - med) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the median for later.
+ median[i] = med
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ sig = sqrt (s / (n2 - 1))
+ else {
+ call sfree (sp)
+ return
+ }
+
+ # Compute individual sigmas and iteratively clip.
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 < max (3, maxkeep+1))
+ next
+ nl = 1
+ nh = n1
+ med = median[i]
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 >= max (MINCLIP, maxkeep+1) && sig > 0.) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (med - Memd[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (Memd[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ s1 = sig * sqrt (max (one, med))
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memd[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memd[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memd[d[n3-1]+k]
+ high = Memd[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memd[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Memd[d[n3-1]+k]
+ high = Memd[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Memd[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memd[d[l]+k] = Memd[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memd[d[l]+k] = Memd[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icaverage.x b/pkg/images/immatch/src/imcombine/src/generic/icaverage.x
new file mode 100644
index 00000000..7167d301
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icaverage.x
@@ -0,0 +1,424 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+include "../icmask.h"
+
+
+# IC_AVERAGE -- Compute the average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_averages (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real sumwt, wt
+real sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Mems[d[1]+k] * wt
+ do j = 2, n[i] {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Mems[d[j]+k] * wt
+ }
+ average[i] = sum
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ sum = Mems[d[1]+k]
+ do j = 2, n[i]
+ sum = sum + Mems[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n[i]
+ else
+ average[i] = sum
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Mems[d[1]+k] * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Mems[d[j]+k] * wt
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sum / sumwt
+ else {
+ sum = Mems[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mems[d[j]+k]
+ average[i] = sum / n1
+ }
+ } else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ sum = Mems[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mems[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n1
+ else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_AVERAGE -- Compute the average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_averagei (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real sumwt, wt
+real sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Memi[d[1]+k] * wt
+ do j = 2, n[i] {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Memi[d[j]+k] * wt
+ }
+ average[i] = sum
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ sum = Memi[d[1]+k]
+ do j = 2, n[i]
+ sum = sum + Memi[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n[i]
+ else
+ average[i] = sum
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Memi[d[1]+k] * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Memi[d[j]+k] * wt
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sum / sumwt
+ else {
+ sum = Memi[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memi[d[j]+k]
+ average[i] = sum / n1
+ }
+ } else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ sum = Memi[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memi[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n1
+ else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_AVERAGE -- Compute the average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_averager (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real sumwt, wt
+real sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Memr[d[1]+k] * wt
+ do j = 2, n[i] {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Memr[d[j]+k] * wt
+ }
+ average[i] = sum
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ sum = Memr[d[1]+k]
+ do j = 2, n[i]
+ sum = sum + Memr[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n[i]
+ else
+ average[i] = sum
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Memr[d[1]+k] * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Memr[d[j]+k] * wt
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sum / sumwt
+ else {
+ sum = Memr[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memr[d[j]+k]
+ average[i] = sum / n1
+ }
+ } else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ sum = Memr[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memr[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n1
+ else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_AVERAGE -- Compute the average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_averaged (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+double average[npts] # Average (returned)
+
+int i, j, k, n1
+real sumwt, wt
+double sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Memd[d[1]+k] * wt
+ do j = 2, n[i] {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Memd[d[j]+k] * wt
+ }
+ average[i] = sum
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ sum = Memd[d[1]+k]
+ do j = 2, n[i]
+ sum = sum + Memd[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n[i]
+ else
+ average[i] = sum
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Memd[d[1]+k] * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Memd[d[j]+k] * wt
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sum / sumwt
+ else {
+ sum = Memd[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memd[d[j]+k]
+ average[i] = sum / n1
+ }
+ } else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ sum = Memd[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memd[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n1
+ else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/iccclip.x b/pkg/images/immatch/src/imcombine/src/generic/iccclip.x
new file mode 100644
index 00000000..cf60c779
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/iccclip.x
@@ -0,0 +1,1791 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 2 # Mininum number of images for algorithm
+
+
+# IC_ACCDCLIP -- Reject pixels using CCD noise parameters about the average
+
+procedure ic_accdclips (d, m, n, scales, zeros, nm, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model parameters
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, r, zero
+data zero /0.0/
+pointer sp, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are no pixels go on to the combining. Since the unweighted
+ # average is computed here possibly skip the combining later.
+
+ # There must be at least max (1, nkeep) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ } else if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # There must be at least two pixels for rejection. The initial
+ # average is the low/high rejected average except in the case of
+ # just two pixels. The rejections are iterated and the average
+ # is recomputed. Corrections for scaling may be performed.
+ # Depending on other flags the image IDs may also need to be adjusted.
+
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (MINCLIP-1, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Mems[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mems[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ repeat {
+ if (n1 == 2) {
+ sum = Mems[d[1]+k]
+ sum = sum + Mems[d[2]+k]
+ a = sum / 2
+ } else {
+ low = Mems[d[1]+k]
+ high = Mems[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Mems[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+ }
+ n2 = n1
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ l = Memi[mp1]
+ s = scales[l]
+ d1 = max (zero, s * (a + zeros[l]))
+ s = sqrt (nm[1,l] + d1/nm[2,l] + (d1*nm[3,l])**2) / s
+
+ d1 = Mems[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, a)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (j=1; j<=n1; j=j+1) {
+ if (keepids) {
+ l = Memi[m[j]+k]
+ s = max (zero, a)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ dp1 = d[j] + k
+ d1 = Mems[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mems[dp1]
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Mems[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mems[dp1]
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Mems[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ }
+
+ n[i] = n1
+ if (!docombine)
+ if (n1 > 0)
+ average[i] = sum / n1
+ else
+ average[i] = blank
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_CCDCLIP -- Reject pixels using CCD noise parameters about the median
+
+procedure ic_mccdclips (d, m, n, scales, zeros, nm, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, mp1, mp2
+real med, zero
+data zero /0.0/
+
+include "../icombine.com"
+
+begin
+ # There must be at least max (MINCLIP, nkeep+1) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0) {
+ med = Mems[d[n3-1]+k]
+ med = (med + Mems[d[n3]+k]) / 2.
+ } else
+ med = Mems[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (med - Mems[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (Mems[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, med)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (; nl <= nh; nl = nl + 1) {
+ if (keepids) {
+ l = Memi[m[nl]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (med - Mems[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ if (keepids) {
+ l = Memi[m[nh]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (Mems[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mems[d[l]+k] = Mems[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mems[d[l]+k] = Mems[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_ACCDCLIP -- Reject pixels using CCD noise parameters about the average
+
+procedure ic_accdclipi (d, m, n, scales, zeros, nm, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model parameters
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, r, zero
+data zero /0.0/
+pointer sp, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are no pixels go on to the combining. Since the unweighted
+ # average is computed here possibly skip the combining later.
+
+ # There must be at least max (1, nkeep) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ } else if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # There must be at least two pixels for rejection. The initial
+ # average is the low/high rejected average except in the case of
+ # just two pixels. The rejections are iterated and the average
+ # is recomputed. Corrections for scaling may be performed.
+ # Depending on other flags the image IDs may also need to be adjusted.
+
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (MINCLIP-1, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memi[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memi[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ repeat {
+ if (n1 == 2) {
+ sum = Memi[d[1]+k]
+ sum = sum + Memi[d[2]+k]
+ a = sum / 2
+ } else {
+ low = Memi[d[1]+k]
+ high = Memi[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memi[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+ }
+ n2 = n1
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ l = Memi[mp1]
+ s = scales[l]
+ d1 = max (zero, s * (a + zeros[l]))
+ s = sqrt (nm[1,l] + d1/nm[2,l] + (d1*nm[3,l])**2) / s
+
+ d1 = Memi[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, a)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (j=1; j<=n1; j=j+1) {
+ if (keepids) {
+ l = Memi[m[j]+k]
+ s = max (zero, a)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ dp1 = d[j] + k
+ d1 = Memi[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memi[dp1]
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memi[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memi[dp1]
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memi[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ }
+
+ n[i] = n1
+ if (!docombine)
+ if (n1 > 0)
+ average[i] = sum / n1
+ else
+ average[i] = blank
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_CCDCLIP -- Reject pixels using CCD noise parameters about the median
+
+procedure ic_mccdclipi (d, m, n, scales, zeros, nm, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, mp1, mp2
+real med, zero
+data zero /0.0/
+
+include "../icombine.com"
+
+begin
+ # There must be at least max (MINCLIP, nkeep+1) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0) {
+ med = Memi[d[n3-1]+k]
+ med = (med + Memi[d[n3]+k]) / 2.
+ } else
+ med = Memi[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (med - Memi[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (Memi[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, med)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (; nl <= nh; nl = nl + 1) {
+ if (keepids) {
+ l = Memi[m[nl]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (med - Memi[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ if (keepids) {
+ l = Memi[m[nh]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (Memi[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memi[d[l]+k] = Memi[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memi[d[l]+k] = Memi[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_ACCDCLIP -- Reject pixels using CCD noise parameters about the average
+
+procedure ic_accdclipr (d, m, n, scales, zeros, nm, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model parameters
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, r, zero
+data zero /0.0/
+pointer sp, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are no pixels go on to the combining. Since the unweighted
+ # average is computed here possibly skip the combining later.
+
+ # There must be at least max (1, nkeep) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ } else if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # There must be at least two pixels for rejection. The initial
+ # average is the low/high rejected average except in the case of
+ # just two pixels. The rejections are iterated and the average
+ # is recomputed. Corrections for scaling may be performed.
+ # Depending on other flags the image IDs may also need to be adjusted.
+
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (MINCLIP-1, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memr[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memr[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ repeat {
+ if (n1 == 2) {
+ sum = Memr[d[1]+k]
+ sum = sum + Memr[d[2]+k]
+ a = sum / 2
+ } else {
+ low = Memr[d[1]+k]
+ high = Memr[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memr[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+ }
+ n2 = n1
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ l = Memi[mp1]
+ s = scales[l]
+ d1 = max (zero, s * (a + zeros[l]))
+ s = sqrt (nm[1,l] + d1/nm[2,l] + (d1*nm[3,l])**2) / s
+
+ d1 = Memr[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, a)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (j=1; j<=n1; j=j+1) {
+ if (keepids) {
+ l = Memi[m[j]+k]
+ s = max (zero, a)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ dp1 = d[j] + k
+ d1 = Memr[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memr[dp1]
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memr[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memr[dp1]
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memr[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ }
+
+ n[i] = n1
+ if (!docombine)
+ if (n1 > 0)
+ average[i] = sum / n1
+ else
+ average[i] = blank
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_CCDCLIP -- Reject pixels using CCD noise parameters about the median
+
+procedure ic_mccdclipr (d, m, n, scales, zeros, nm, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, mp1, mp2
+real med, zero
+data zero /0.0/
+
+include "../icombine.com"
+
+begin
+ # There must be at least max (MINCLIP, nkeep+1) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0) {
+ med = Memr[d[n3-1]+k]
+ med = (med + Memr[d[n3]+k]) / 2.
+ } else
+ med = Memr[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (med - Memr[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (Memr[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, med)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (; nl <= nh; nl = nl + 1) {
+ if (keepids) {
+ l = Memi[m[nl]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (med - Memr[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ if (keepids) {
+ l = Memi[m[nh]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (Memr[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memr[d[l]+k] = Memr[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memr[d[l]+k] = Memr[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_ACCDCLIP -- Reject pixels using CCD noise parameters about the average
+
+procedure ic_accdclipd (d, m, n, scales, zeros, nm, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model parameters
+int nimages # Number of images
+int npts # Number of output points per line
+double average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+double d1, low, high, sum, a, s, r, zero
+data zero /0.0D0/
+pointer sp, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are no pixels go on to the combining. Since the unweighted
+ # average is computed here possibly skip the combining later.
+
+ # There must be at least max (1, nkeep) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ } else if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # There must be at least two pixels for rejection. The initial
+ # average is the low/high rejected average except in the case of
+ # just two pixels. The rejections are iterated and the average
+ # is recomputed. Corrections for scaling may be performed.
+ # Depending on other flags the image IDs may also need to be adjusted.
+
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (MINCLIP-1, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memd[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memd[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ repeat {
+ if (n1 == 2) {
+ sum = Memd[d[1]+k]
+ sum = sum + Memd[d[2]+k]
+ a = sum / 2
+ } else {
+ low = Memd[d[1]+k]
+ high = Memd[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memd[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+ }
+ n2 = n1
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ l = Memi[mp1]
+ s = scales[l]
+ d1 = max (zero, s * (a + zeros[l]))
+ s = sqrt (nm[1,l] + d1/nm[2,l] + (d1*nm[3,l])**2) / s
+
+ d1 = Memd[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, a)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (j=1; j<=n1; j=j+1) {
+ if (keepids) {
+ l = Memi[m[j]+k]
+ s = max (zero, a)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ dp1 = d[j] + k
+ d1 = Memd[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memd[dp1]
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memd[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memd[dp1]
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memd[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ }
+
+ n[i] = n1
+ if (!docombine)
+ if (n1 > 0)
+ average[i] = sum / n1
+ else
+ average[i] = blank
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_CCDCLIP -- Reject pixels using CCD noise parameters about the median
+
+procedure ic_mccdclipd (d, m, n, scales, zeros, nm, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model
+int nimages # Number of images
+int npts # Number of output points per line
+double median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, mp1, mp2
+double med, zero
+data zero /0.0D0/
+
+include "../icombine.com"
+
+begin
+ # There must be at least max (MINCLIP, nkeep+1) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0) {
+ med = Memd[d[n3-1]+k]
+ med = (med + Memd[d[n3]+k]) / 2.
+ } else
+ med = Memd[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (med - Memd[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (Memd[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, med)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (; nl <= nh; nl = nl + 1) {
+ if (keepids) {
+ l = Memi[m[nl]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (med - Memd[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ if (keepids) {
+ l = Memi[m[nh]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (Memd[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memd[d[l]+k] = Memd[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memd[d[l]+k] = Memd[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icgdata.x b/pkg/images/immatch/src/imcombine/src/generic/icgdata.x
new file mode 100644
index 00000000..774de63c
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icgdata.x
@@ -0,0 +1,1531 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+
+
+# IC_GDATA -- Get line of image and mask data and apply threshold and scaling.
+# Entirely empty lines are excluded. The data are compacted within the
+# input data buffers. If it is required, the connection to the original
+# image index is kept in the returned m data pointers.
+
+procedure ic_gdatas (in, out, dbuf, d, id, n, m, lflag, offsets, scales,
+ zeros, nimages, npts, v1, v2)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+pointer dbuf[nimages] # Data buffers
+pointer d[nimages] # Data pointers
+pointer id[nimages] # ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Empty mask flags
+int offsets[nimages,ARB] # Image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+int nimages # Number of input images
+int npts # NUmber of output points per line
+long v1[ARB], v2[ARB] # Line vectors
+
+short temp
+int i, j, k, l, n1, n2, npix, nin, nout, ndim, nused, mtype, xt_imgnls()
+real a, b
+pointer buf, dp, ip, mp
+errchk xt_cpix, xt_imgnls
+
+short max_pixel
+data max_pixel/MAX_SHORT/
+
+include "../icombine.com"
+
+begin
+ # Get masks and return if there is no data
+ call ic_mget (in, out, offsets, v1, v2, m, lflag, nimages, mtype)
+ if (dflag == D_NONE) {
+ call aclri (n, npts)
+ return
+ }
+
+ # Close images which are not needed.
+ nout = IM_LEN(out[1],1)
+ ndim = IM_NDIM(out[1])
+ if (!project && ndim < 3) {
+ do i = 1, nimages {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1)
+ call xt_cpix (i)
+ if (ndim > 1) {
+ j = v1[2] - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+ }
+ }
+
+ # Get data and fill data buffers. Correct for offsets if needed.
+ do i = 1, nimages {
+ if (lflag[i] == D_NONE)
+ next
+ if (dbuf[i] == NULL) {
+ call amovl (v1, v2, IM_MAXDIM)
+ if (project)
+ v2[ndim+1] = i
+ j = xt_imgnls (in[i], i, d[i], v2, v1[2])
+ } else {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1) {
+ lflag[i] = D_NONE
+ next
+ }
+ k = 1 + j - offsets[i,1]
+ v2[1] = k
+ do l = 2, ndim {
+ v2[l] = v1[l] - offsets[i,l]
+ if (v2[l] < 1 || v2[l] > IM_LEN(in[i],l)) {
+ lflag[i] = D_NONE
+ break
+ }
+ }
+ if (lflag[i] == D_NONE)
+ next
+ if (project)
+ v2[ndim+1] = i
+ l = xt_imgnls (in[i], i, buf, v2, v1[2])
+ call amovs (Mems[buf+k-1], Mems[dbuf[i]+j], npix)
+ d[i] = dbuf[i]
+ }
+ }
+
+ # Set values to max_pixel if needed.
+ if (mtype == M_NOVAL) {
+ do i = 1, nimages {
+ dp = d[i]; mp = m[i]
+ if (lflag[i] == D_NONE || dp == NULL)
+ next
+ else if (lflag[i] == D_MIX) {
+ do j = 1, npts {
+ if (Memi[mp] == 1)
+ Mems[dp] = max_pixel
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply threshold if needed
+ if (dothresh) {
+ do i = 1, nimages {
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ a = Mems[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+
+ lflag[i] = D_MIX
+ dflag = D_MIX
+ }
+ dp = dp + 1
+ }
+
+ # Check for completely empty lines
+ if (lflag[i] == D_MIX) {
+ lflag[i] = D_NONE
+ mp = m[i]
+ do j = 1, npts {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1) {
+ a = Mems[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+ dflag = D_MIX
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+
+ # Check for completely empty lines
+ lflag[i] = D_NONE
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply scaling (avoiding masked pixels which might overflow?)
+ if (doscale) {
+ if (dflag == D_ALL) {
+ do i = 1, nimages {
+ dp = d[i]
+ a = scales[i]
+ b = -zeros[i]
+ do j = 1, npts {
+ Mems[dp] = Mems[dp] / a + b
+ dp = dp + 1
+ }
+ }
+ } else if (dflag == D_MIX) {
+ do i = 1, nimages {
+ a = scales[i]
+ b = -zeros[i]
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ Mems[dp] = Mems[dp] / a + b
+ dp = dp + 1
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1)
+ Mems[dp] = Mems[dp] / a + b
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+ }
+
+ # Sort pointers to exclude unused images.
+ # Use the lflag array to keep track of the image index.
+
+ if (dflag == D_ALL)
+ nused = nimages
+ else {
+ nused = 0
+ do i = 1, nimages {
+ if (lflag[i] != D_NONE) {
+ nused = nused + 1
+ d[nused] = d[i]
+ m[nused] = m[i]
+ lflag[nused] = i
+ }
+ }
+ do i = nused+1, nimages
+ d[i] = NULL
+ if (nused == 0)
+ dflag = D_NONE
+ }
+
+ # Compact data to remove bad pixels
+ # Keep track of the image indices if needed
+ # If growing mark the end of the included image indices with zero
+
+ if (dflag == D_ALL) {
+ call amovki (nused, n, npts)
+ if (keepids)
+ do i = 1, nimages
+ call amovki (i, Memi[id[i]], npts)
+ } else if (dflag == D_NONE)
+ call aclri (n, npts)
+ else {
+ call aclri (n, npts)
+ if (keepids) {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ ip = id[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ Memi[ip] = l
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Mems[d[k]+j-1]
+ Mems[d[k]+j-1] = Mems[dp]
+ Mems[dp] = temp
+ Memi[ip] = Memi[id[k]+j-1]
+ Memi[id[k]+j-1] = l
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ } else
+ Memi[ip] = 0
+ dp = dp + 1
+ ip = ip + 1
+ mp = mp + 1
+ }
+ }
+ if (grow >= 1.) {
+ do j = 1, npts {
+ do i = n[j]+1, nimages
+ Memi[id[i]+j-1] = 0
+ }
+ }
+ } else {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Mems[d[k]+j-1]
+ Mems[d[k]+j-1] = Mems[dp]
+ Mems[dp] = temp
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Sort the pixels and IDs if needed
+ if (mclip) {
+ call malloc (dp, nused, TY_SHORT)
+ if (keepids) {
+ call malloc (ip, nused, TY_INT)
+ call ic_2sorts (d, Mems[dp], id, Memi[ip], n, npts)
+ call mfree (ip, TY_INT)
+ } else
+ call ic_sorts (d, Mems[dp], n, npts)
+ call mfree (dp, TY_SHORT)
+ }
+
+ # If no good pixels set the number of usable values as -n and
+ # shift them to lower values.
+ if (mtype == M_NOVAL) {
+ if (keepids) {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ ip = id[i] + j - 1
+ if (Mems[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i) {
+ Mems[d[k]+j-1] = Mems[dp]
+ Memi[id[k]+j-1] = Memi[ip]
+ }
+ }
+ }
+ }
+ } else {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ if (Mems[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i)
+ Mems[d[k]+j-1] = Mems[dp]
+ }
+ }
+ }
+ }
+ }
+end
+
+# IC_GDATA -- Get line of image and mask data and apply threshold and scaling.
+# Entirely empty lines are excluded. The data are compacted within the
+# input data buffers. If it is required, the connection to the original
+# image index is kept in the returned m data pointers.
+
+procedure ic_gdatai (in, out, dbuf, d, id, n, m, lflag, offsets, scales,
+ zeros, nimages, npts, v1, v2)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+pointer dbuf[nimages] # Data buffers
+pointer d[nimages] # Data pointers
+pointer id[nimages] # ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Empty mask flags
+int offsets[nimages,ARB] # Image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+int nimages # Number of input images
+int npts # NUmber of output points per line
+long v1[ARB], v2[ARB] # Line vectors
+
+int temp
+int i, j, k, l, n1, n2, npix, nin, nout, ndim, nused, mtype, xt_imgnli()
+real a, b
+pointer buf, dp, ip, mp
+errchk xt_cpix, xt_imgnli
+
+int max_pixel
+data max_pixel/MAX_INT/
+
+include "../icombine.com"
+
+begin
+ # Get masks and return if there is no data
+ call ic_mget (in, out, offsets, v1, v2, m, lflag, nimages, mtype)
+ if (dflag == D_NONE) {
+ call aclri (n, npts)
+ return
+ }
+
+ # Close images which are not needed.
+ nout = IM_LEN(out[1],1)
+ ndim = IM_NDIM(out[1])
+ if (!project && ndim < 3) {
+ do i = 1, nimages {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1)
+ call xt_cpix (i)
+ if (ndim > 1) {
+ j = v1[2] - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+ }
+ }
+
+ # Get data and fill data buffers. Correct for offsets if needed.
+ do i = 1, nimages {
+ if (lflag[i] == D_NONE)
+ next
+ if (dbuf[i] == NULL) {
+ call amovl (v1, v2, IM_MAXDIM)
+ if (project)
+ v2[ndim+1] = i
+ j = xt_imgnli (in[i], i, d[i], v2, v1[2])
+ } else {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1) {
+ lflag[i] = D_NONE
+ next
+ }
+ k = 1 + j - offsets[i,1]
+ v2[1] = k
+ do l = 2, ndim {
+ v2[l] = v1[l] - offsets[i,l]
+ if (v2[l] < 1 || v2[l] > IM_LEN(in[i],l)) {
+ lflag[i] = D_NONE
+ break
+ }
+ }
+ if (lflag[i] == D_NONE)
+ next
+ if (project)
+ v2[ndim+1] = i
+ l = xt_imgnli (in[i], i, buf, v2, v1[2])
+ call amovi (Memi[buf+k-1], Memi[dbuf[i]+j], npix)
+ d[i] = dbuf[i]
+ }
+ }
+
+ # Set values to max_pixel if needed.
+ if (mtype == M_NOVAL) {
+ do i = 1, nimages {
+ dp = d[i]; mp = m[i]
+ if (lflag[i] == D_NONE || dp == NULL)
+ next
+ else if (lflag[i] == D_MIX) {
+ do j = 1, npts {
+ if (Memi[mp] == 1)
+ Memi[dp] = max_pixel
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply threshold if needed
+ if (dothresh) {
+ do i = 1, nimages {
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ a = Memi[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+
+ lflag[i] = D_MIX
+ dflag = D_MIX
+ }
+ dp = dp + 1
+ }
+
+ # Check for completely empty lines
+ if (lflag[i] == D_MIX) {
+ lflag[i] = D_NONE
+ mp = m[i]
+ do j = 1, npts {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1) {
+ a = Memi[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+ dflag = D_MIX
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+
+ # Check for completely empty lines
+ lflag[i] = D_NONE
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply scaling (avoiding masked pixels which might overflow?)
+ if (doscale) {
+ if (dflag == D_ALL) {
+ do i = 1, nimages {
+ dp = d[i]
+ a = scales[i]
+ b = -zeros[i]
+ do j = 1, npts {
+ Memi[dp] = Memi[dp] / a + b
+ dp = dp + 1
+ }
+ }
+ } else if (dflag == D_MIX) {
+ do i = 1, nimages {
+ a = scales[i]
+ b = -zeros[i]
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ Memi[dp] = Memi[dp] / a + b
+ dp = dp + 1
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1)
+ Memi[dp] = Memi[dp] / a + b
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+ }
+
+ # Sort pointers to exclude unused images.
+ # Use the lflag array to keep track of the image index.
+
+ if (dflag == D_ALL)
+ nused = nimages
+ else {
+ nused = 0
+ do i = 1, nimages {
+ if (lflag[i] != D_NONE) {
+ nused = nused + 1
+ d[nused] = d[i]
+ m[nused] = m[i]
+ lflag[nused] = i
+ }
+ }
+ do i = nused+1, nimages
+ d[i] = NULL
+ if (nused == 0)
+ dflag = D_NONE
+ }
+
+ # Compact data to remove bad pixels
+ # Keep track of the image indices if needed
+ # If growing mark the end of the included image indices with zero
+
+ if (dflag == D_ALL) {
+ call amovki (nused, n, npts)
+ if (keepids)
+ do i = 1, nimages
+ call amovki (i, Memi[id[i]], npts)
+ } else if (dflag == D_NONE)
+ call aclri (n, npts)
+ else {
+ call aclri (n, npts)
+ if (keepids) {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ ip = id[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ Memi[ip] = l
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Memi[d[k]+j-1]
+ Memi[d[k]+j-1] = Memi[dp]
+ Memi[dp] = temp
+ Memi[ip] = Memi[id[k]+j-1]
+ Memi[id[k]+j-1] = l
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ } else
+ Memi[ip] = 0
+ dp = dp + 1
+ ip = ip + 1
+ mp = mp + 1
+ }
+ }
+ if (grow >= 1.) {
+ do j = 1, npts {
+ do i = n[j]+1, nimages
+ Memi[id[i]+j-1] = 0
+ }
+ }
+ } else {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Memi[d[k]+j-1]
+ Memi[d[k]+j-1] = Memi[dp]
+ Memi[dp] = temp
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Sort the pixels and IDs if needed
+ if (mclip) {
+ call malloc (dp, nused, TY_INT)
+ if (keepids) {
+ call malloc (ip, nused, TY_INT)
+ call ic_2sorti (d, Memi[dp], id, Memi[ip], n, npts)
+ call mfree (ip, TY_INT)
+ } else
+ call ic_sorti (d, Memi[dp], n, npts)
+ call mfree (dp, TY_INT)
+ }
+
+ # If no good pixels set the number of usable values as -n and
+ # shift them to lower values.
+ if (mtype == M_NOVAL) {
+ if (keepids) {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ ip = id[i] + j - 1
+ if (Memi[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i) {
+ Memi[d[k]+j-1] = Memi[dp]
+ Memi[id[k]+j-1] = Memi[ip]
+ }
+ }
+ }
+ }
+ } else {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ if (Memi[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i)
+ Memi[d[k]+j-1] = Memi[dp]
+ }
+ }
+ }
+ }
+ }
+end
+
+# IC_GDATA -- Get line of image and mask data and apply threshold and scaling.
+# Entirely empty lines are excluded. The data are compacted within the
+# input data buffers. If it is required, the connection to the original
+# image index is kept in the returned m data pointers.
+
+procedure ic_gdatar (in, out, dbuf, d, id, n, m, lflag, offsets, scales,
+ zeros, nimages, npts, v1, v2)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+pointer dbuf[nimages] # Data buffers
+pointer d[nimages] # Data pointers
+pointer id[nimages] # ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Empty mask flags
+int offsets[nimages,ARB] # Image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+int nimages # Number of input images
+int npts # NUmber of output points per line
+long v1[ARB], v2[ARB] # Line vectors
+
+real temp
+int i, j, k, l, n1, n2, npix, nin, nout, ndim, nused, mtype, xt_imgnlr()
+real a, b
+pointer buf, dp, ip, mp
+errchk xt_cpix, xt_imgnlr
+
+real max_pixel
+data max_pixel/MAX_REAL/
+
+include "../icombine.com"
+
+begin
+ # Get masks and return if there is no data
+ call ic_mget (in, out, offsets, v1, v2, m, lflag, nimages, mtype)
+ if (dflag == D_NONE) {
+ call aclri (n, npts)
+ return
+ }
+
+ # Close images which are not needed.
+ nout = IM_LEN(out[1],1)
+ ndim = IM_NDIM(out[1])
+ if (!project && ndim < 3) {
+ do i = 1, nimages {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1)
+ call xt_cpix (i)
+ if (ndim > 1) {
+ j = v1[2] - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+ }
+ }
+
+ # Get data and fill data buffers. Correct for offsets if needed.
+ do i = 1, nimages {
+ if (lflag[i] == D_NONE)
+ next
+ if (dbuf[i] == NULL) {
+ call amovl (v1, v2, IM_MAXDIM)
+ if (project)
+ v2[ndim+1] = i
+ j = xt_imgnlr (in[i], i, d[i], v2, v1[2])
+ } else {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1) {
+ lflag[i] = D_NONE
+ next
+ }
+ k = 1 + j - offsets[i,1]
+ v2[1] = k
+ do l = 2, ndim {
+ v2[l] = v1[l] - offsets[i,l]
+ if (v2[l] < 1 || v2[l] > IM_LEN(in[i],l)) {
+ lflag[i] = D_NONE
+ break
+ }
+ }
+ if (lflag[i] == D_NONE)
+ next
+ if (project)
+ v2[ndim+1] = i
+ l = xt_imgnlr (in[i], i, buf, v2, v1[2])
+ call amovr (Memr[buf+k-1], Memr[dbuf[i]+j], npix)
+ d[i] = dbuf[i]
+ }
+ }
+
+ # Set values to max_pixel if needed.
+ if (mtype == M_NOVAL) {
+ do i = 1, nimages {
+ dp = d[i]; mp = m[i]
+ if (lflag[i] == D_NONE || dp == NULL)
+ next
+ else if (lflag[i] == D_MIX) {
+ do j = 1, npts {
+ if (Memi[mp] == 1)
+ Memr[dp] = max_pixel
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply threshold if needed
+ if (dothresh) {
+ do i = 1, nimages {
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ a = Memr[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+
+ lflag[i] = D_MIX
+ dflag = D_MIX
+ }
+ dp = dp + 1
+ }
+
+ # Check for completely empty lines
+ if (lflag[i] == D_MIX) {
+ lflag[i] = D_NONE
+ mp = m[i]
+ do j = 1, npts {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1) {
+ a = Memr[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+ dflag = D_MIX
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+
+ # Check for completely empty lines
+ lflag[i] = D_NONE
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply scaling (avoiding masked pixels which might overflow?)
+ if (doscale) {
+ if (dflag == D_ALL) {
+ do i = 1, nimages {
+ dp = d[i]
+ a = scales[i]
+ b = -zeros[i]
+ do j = 1, npts {
+ Memr[dp] = Memr[dp] / a + b
+ dp = dp + 1
+ }
+ }
+ } else if (dflag == D_MIX) {
+ do i = 1, nimages {
+ a = scales[i]
+ b = -zeros[i]
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ Memr[dp] = Memr[dp] / a + b
+ dp = dp + 1
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1)
+ Memr[dp] = Memr[dp] / a + b
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+ }
+
+ # Sort pointers to exclude unused images.
+ # Use the lflag array to keep track of the image index.
+
+ if (dflag == D_ALL)
+ nused = nimages
+ else {
+ nused = 0
+ do i = 1, nimages {
+ if (lflag[i] != D_NONE) {
+ nused = nused + 1
+ d[nused] = d[i]
+ m[nused] = m[i]
+ lflag[nused] = i
+ }
+ }
+ do i = nused+1, nimages
+ d[i] = NULL
+ if (nused == 0)
+ dflag = D_NONE
+ }
+
+ # Compact data to remove bad pixels
+ # Keep track of the image indices if needed
+ # If growing mark the end of the included image indices with zero
+
+ if (dflag == D_ALL) {
+ call amovki (nused, n, npts)
+ if (keepids)
+ do i = 1, nimages
+ call amovki (i, Memi[id[i]], npts)
+ } else if (dflag == D_NONE)
+ call aclri (n, npts)
+ else {
+ call aclri (n, npts)
+ if (keepids) {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ ip = id[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ Memi[ip] = l
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Memr[d[k]+j-1]
+ Memr[d[k]+j-1] = Memr[dp]
+ Memr[dp] = temp
+ Memi[ip] = Memi[id[k]+j-1]
+ Memi[id[k]+j-1] = l
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ } else
+ Memi[ip] = 0
+ dp = dp + 1
+ ip = ip + 1
+ mp = mp + 1
+ }
+ }
+ if (grow >= 1.) {
+ do j = 1, npts {
+ do i = n[j]+1, nimages
+ Memi[id[i]+j-1] = 0
+ }
+ }
+ } else {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Memr[d[k]+j-1]
+ Memr[d[k]+j-1] = Memr[dp]
+ Memr[dp] = temp
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Sort the pixels and IDs if needed
+ if (mclip) {
+ call malloc (dp, nused, TY_REAL)
+ if (keepids) {
+ call malloc (ip, nused, TY_INT)
+ call ic_2sortr (d, Memr[dp], id, Memi[ip], n, npts)
+ call mfree (ip, TY_INT)
+ } else
+ call ic_sortr (d, Memr[dp], n, npts)
+ call mfree (dp, TY_REAL)
+ }
+
+ # If no good pixels set the number of usable values as -n and
+ # shift them to lower values.
+ if (mtype == M_NOVAL) {
+ if (keepids) {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ ip = id[i] + j - 1
+ if (Memr[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i) {
+ Memr[d[k]+j-1] = Memr[dp]
+ Memi[id[k]+j-1] = Memi[ip]
+ }
+ }
+ }
+ }
+ } else {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ if (Memr[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i)
+ Memr[d[k]+j-1] = Memr[dp]
+ }
+ }
+ }
+ }
+ }
+end
+
+# IC_GDATA -- Get line of image and mask data and apply threshold and scaling.
+# Entirely empty lines are excluded. The data are compacted within the
+# input data buffers. If it is required, the connection to the original
+# image index is kept in the returned m data pointers.
+
+procedure ic_gdatad (in, out, dbuf, d, id, n, m, lflag, offsets, scales,
+ zeros, nimages, npts, v1, v2)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+pointer dbuf[nimages] # Data buffers
+pointer d[nimages] # Data pointers
+pointer id[nimages] # ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Empty mask flags
+int offsets[nimages,ARB] # Image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+int nimages # Number of input images
+int npts # NUmber of output points per line
+long v1[ARB], v2[ARB] # Line vectors
+
+double temp
+int i, j, k, l, n1, n2, npix, nin, nout, ndim, nused, mtype, xt_imgnld()
+real a, b
+pointer buf, dp, ip, mp
+errchk xt_cpix, xt_imgnld
+
+double max_pixel
+data max_pixel/MAX_DOUBLE/
+
+include "../icombine.com"
+
+begin
+ # Get masks and return if there is no data
+ call ic_mget (in, out, offsets, v1, v2, m, lflag, nimages, mtype)
+ if (dflag == D_NONE) {
+ call aclri (n, npts)
+ return
+ }
+
+ # Close images which are not needed.
+ nout = IM_LEN(out[1],1)
+ ndim = IM_NDIM(out[1])
+ if (!project && ndim < 3) {
+ do i = 1, nimages {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1)
+ call xt_cpix (i)
+ if (ndim > 1) {
+ j = v1[2] - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+ }
+ }
+
+ # Get data and fill data buffers. Correct for offsets if needed.
+ do i = 1, nimages {
+ if (lflag[i] == D_NONE)
+ next
+ if (dbuf[i] == NULL) {
+ call amovl (v1, v2, IM_MAXDIM)
+ if (project)
+ v2[ndim+1] = i
+ j = xt_imgnld (in[i], i, d[i], v2, v1[2])
+ } else {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1) {
+ lflag[i] = D_NONE
+ next
+ }
+ k = 1 + j - offsets[i,1]
+ v2[1] = k
+ do l = 2, ndim {
+ v2[l] = v1[l] - offsets[i,l]
+ if (v2[l] < 1 || v2[l] > IM_LEN(in[i],l)) {
+ lflag[i] = D_NONE
+ break
+ }
+ }
+ if (lflag[i] == D_NONE)
+ next
+ if (project)
+ v2[ndim+1] = i
+ l = xt_imgnld (in[i], i, buf, v2, v1[2])
+ call amovd (Memd[buf+k-1], Memd[dbuf[i]+j], npix)
+ d[i] = dbuf[i]
+ }
+ }
+
+ # Set values to max_pixel if needed.
+ if (mtype == M_NOVAL) {
+ do i = 1, nimages {
+ dp = d[i]; mp = m[i]
+ if (lflag[i] == D_NONE || dp == NULL)
+ next
+ else if (lflag[i] == D_MIX) {
+ do j = 1, npts {
+ if (Memi[mp] == 1)
+ Memd[dp] = max_pixel
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply threshold if needed
+ if (dothresh) {
+ do i = 1, nimages {
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ a = Memd[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+
+ lflag[i] = D_MIX
+ dflag = D_MIX
+ }
+ dp = dp + 1
+ }
+
+ # Check for completely empty lines
+ if (lflag[i] == D_MIX) {
+ lflag[i] = D_NONE
+ mp = m[i]
+ do j = 1, npts {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1) {
+ a = Memd[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+ dflag = D_MIX
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+
+ # Check for completely empty lines
+ lflag[i] = D_NONE
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply scaling (avoiding masked pixels which might overflow?)
+ if (doscale) {
+ if (dflag == D_ALL) {
+ do i = 1, nimages {
+ dp = d[i]
+ a = scales[i]
+ b = -zeros[i]
+ do j = 1, npts {
+ Memd[dp] = Memd[dp] / a + b
+ dp = dp + 1
+ }
+ }
+ } else if (dflag == D_MIX) {
+ do i = 1, nimages {
+ a = scales[i]
+ b = -zeros[i]
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ Memd[dp] = Memd[dp] / a + b
+ dp = dp + 1
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1)
+ Memd[dp] = Memd[dp] / a + b
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+ }
+
+ # Sort pointers to exclude unused images.
+ # Use the lflag array to keep track of the image index.
+
+ if (dflag == D_ALL)
+ nused = nimages
+ else {
+ nused = 0
+ do i = 1, nimages {
+ if (lflag[i] != D_NONE) {
+ nused = nused + 1
+ d[nused] = d[i]
+ m[nused] = m[i]
+ lflag[nused] = i
+ }
+ }
+ do i = nused+1, nimages
+ d[i] = NULL
+ if (nused == 0)
+ dflag = D_NONE
+ }
+
+ # Compact data to remove bad pixels
+ # Keep track of the image indices if needed
+ # If growing mark the end of the included image indices with zero
+
+ if (dflag == D_ALL) {
+ call amovki (nused, n, npts)
+ if (keepids)
+ do i = 1, nimages
+ call amovki (i, Memi[id[i]], npts)
+ } else if (dflag == D_NONE)
+ call aclri (n, npts)
+ else {
+ call aclri (n, npts)
+ if (keepids) {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ ip = id[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ Memi[ip] = l
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Memd[d[k]+j-1]
+ Memd[d[k]+j-1] = Memd[dp]
+ Memd[dp] = temp
+ Memi[ip] = Memi[id[k]+j-1]
+ Memi[id[k]+j-1] = l
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ } else
+ Memi[ip] = 0
+ dp = dp + 1
+ ip = ip + 1
+ mp = mp + 1
+ }
+ }
+ if (grow >= 1.) {
+ do j = 1, npts {
+ do i = n[j]+1, nimages
+ Memi[id[i]+j-1] = 0
+ }
+ }
+ } else {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Memd[d[k]+j-1]
+ Memd[d[k]+j-1] = Memd[dp]
+ Memd[dp] = temp
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Sort the pixels and IDs if needed
+ if (mclip) {
+ call malloc (dp, nused, TY_DOUBLE)
+ if (keepids) {
+ call malloc (ip, nused, TY_INT)
+ call ic_2sortd (d, Memd[dp], id, Memi[ip], n, npts)
+ call mfree (ip, TY_INT)
+ } else
+ call ic_sortd (d, Memd[dp], n, npts)
+ call mfree (dp, TY_DOUBLE)
+ }
+
+ # If no good pixels set the number of usable values as -n and
+ # shift them to lower values.
+ if (mtype == M_NOVAL) {
+ if (keepids) {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ ip = id[i] + j - 1
+ if (Memd[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i) {
+ Memd[d[k]+j-1] = Memd[dp]
+ Memi[id[k]+j-1] = Memi[ip]
+ }
+ }
+ }
+ }
+ } else {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ if (Memd[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i)
+ Memd[d[k]+j-1] = Memd[dp]
+ }
+ }
+ }
+ }
+ }
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icgrow.x b/pkg/images/immatch/src/imcombine/src/generic/icgrow.x
new file mode 100644
index 00000000..1ccb7885
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icgrow.x
@@ -0,0 +1,263 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <pmset.h>
+include "../icombine.h"
+
+# IC_GROW -- Mark neigbors of rejected pixels.
+# The rejected pixels (original plus grown) are saved in pixel masks.
+
+procedure ic_grow (out, v, m, n, buf, nimages, npts, pms)
+
+pointer out # Output image pointer
+long v[ARB] # Output vector
+pointer m[ARB] # Image id pointers
+int n[ARB] # Number of good pixels
+int buf[npts,nimages] # Working buffer
+int nimages # Number of images
+int npts # Number of output points per line
+pointer pms # Pointer to array of pixel masks
+
+int i, j, k, l, line, nl, rop, igrow, nset, ncompress, or()
+real grow2, i2
+pointer mp, pm, pm_newmask()
+errchk pm_newmask()
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_NONE || grow == 0.)
+ return
+
+ line = v[2]
+ nl = IM_LEN(out,2)
+ rop = or (PIX_SRC, PIX_DST)
+
+ igrow = grow
+ grow2 = grow**2
+ do l = 0, igrow {
+ i2 = grow2 - l * l
+ call aclri (buf, npts*nimages)
+ nset = 0
+ do j = 1, npts {
+ do k = n[j]+1, nimages {
+ mp = Memi[m[k]+j-1]
+ if (mp == 0)
+ next
+ do i = 0, igrow {
+ if (i**2 > i2)
+ next
+ if (j > i)
+ buf[j-i,mp] = 1
+ if (j+i <= npts)
+ buf[j+i,mp] = 1
+ nset = nset + 1
+ }
+ }
+ }
+ if (nset == 0)
+ return
+
+ if (pms == NULL) {
+ call malloc (pms, nimages, TY_POINTER)
+ do i = 1, nimages
+ Memi[pms+i-1] = pm_newmask (out, 1)
+ ncompress = 0
+ }
+ do i = 1, nimages {
+ pm = Memi[pms+i-1]
+ v[2] = line - l
+ if (v[2] > 0)
+ call pmplpi (pm, v, buf[1,i], 1, npts, rop)
+ if (l > 0) {
+ v[2] = line + l
+ if (v[2] <= nl)
+ call pmplpi (pm, v, buf[1,i], 1, npts, rop)
+ }
+ }
+ }
+ v[2] = line
+
+ if (ncompress > 10) {
+ do i = 1, nimages {
+ pm = Memi[pms+i-1]
+ call pm_compress (pm)
+ }
+ ncompress = 0
+ } else
+ ncompress = ncompress + 1
+end
+
+
+
+# IC_GROW$T -- Reject pixels.
+
+procedure ic_grows (v, d, m, n, buf, nimages, npts, pms)
+
+long v[ARB] # Output vector
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[ARB] # Number of good pixels
+int buf[ARB] # Buffer of npts
+int nimages # Number of images
+int npts # Number of output points per line
+pointer pms # Pointer to array of pixel masks
+
+int i, j, k
+pointer pm
+bool pl_linenotempty()
+
+include "../icombine.com"
+
+begin
+ do k = 1, nimages {
+ pm = Memi[pms+k-1]
+ if (!pl_linenotempty (pm, v))
+ next
+ call pmglpi (pm, v, buf, 1, npts, PIX_SRC)
+ do i = 1, npts {
+ if (buf[i] == 0)
+ next
+ for (j = 1; j <= n[i]; j = j + 1) {
+ if (Memi[m[j]+i-1] == k) {
+ if (j < n[i]) {
+ Mems[d[j]+i-1] = Mems[d[n[i]]+i-1]
+ Memi[m[j]+i-1] = Memi[m[n[i]]+i-1]
+ }
+ n[i] = n[i] - 1
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+ }
+end
+
+# IC_GROW$T -- Reject pixels.
+
+procedure ic_growi (v, d, m, n, buf, nimages, npts, pms)
+
+long v[ARB] # Output vector
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[ARB] # Number of good pixels
+int buf[ARB] # Buffer of npts
+int nimages # Number of images
+int npts # Number of output points per line
+pointer pms # Pointer to array of pixel masks
+
+int i, j, k
+pointer pm
+bool pl_linenotempty()
+
+include "../icombine.com"
+
+begin
+ do k = 1, nimages {
+ pm = Memi[pms+k-1]
+ if (!pl_linenotempty (pm, v))
+ next
+ call pmglpi (pm, v, buf, 1, npts, PIX_SRC)
+ do i = 1, npts {
+ if (buf[i] == 0)
+ next
+ for (j = 1; j <= n[i]; j = j + 1) {
+ if (Memi[m[j]+i-1] == k) {
+ if (j < n[i]) {
+ Memi[d[j]+i-1] = Memi[d[n[i]]+i-1]
+ Memi[m[j]+i-1] = Memi[m[n[i]]+i-1]
+ }
+ n[i] = n[i] - 1
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+ }
+end
+
+# IC_GROW$T -- Reject pixels.
+
+procedure ic_growr (v, d, m, n, buf, nimages, npts, pms)
+
+long v[ARB] # Output vector
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[ARB] # Number of good pixels
+int buf[ARB] # Buffer of npts
+int nimages # Number of images
+int npts # Number of output points per line
+pointer pms # Pointer to array of pixel masks
+
+int i, j, k
+pointer pm
+bool pl_linenotempty()
+
+include "../icombine.com"
+
+begin
+ do k = 1, nimages {
+ pm = Memi[pms+k-1]
+ if (!pl_linenotempty (pm, v))
+ next
+ call pmglpi (pm, v, buf, 1, npts, PIX_SRC)
+ do i = 1, npts {
+ if (buf[i] == 0)
+ next
+ for (j = 1; j <= n[i]; j = j + 1) {
+ if (Memi[m[j]+i-1] == k) {
+ if (j < n[i]) {
+ Memr[d[j]+i-1] = Memr[d[n[i]]+i-1]
+ Memi[m[j]+i-1] = Memi[m[n[i]]+i-1]
+ }
+ n[i] = n[i] - 1
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+ }
+end
+
+# IC_GROW$T -- Reject pixels.
+
+procedure ic_growd (v, d, m, n, buf, nimages, npts, pms)
+
+long v[ARB] # Output vector
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[ARB] # Number of good pixels
+int buf[ARB] # Buffer of npts
+int nimages # Number of images
+int npts # Number of output points per line
+pointer pms # Pointer to array of pixel masks
+
+int i, j, k
+pointer pm
+bool pl_linenotempty()
+
+include "../icombine.com"
+
+begin
+ do k = 1, nimages {
+ pm = Memi[pms+k-1]
+ if (!pl_linenotempty (pm, v))
+ next
+ call pmglpi (pm, v, buf, 1, npts, PIX_SRC)
+ do i = 1, npts {
+ if (buf[i] == 0)
+ next
+ for (j = 1; j <= n[i]; j = j + 1) {
+ if (Memi[m[j]+i-1] == k) {
+ if (j < n[i]) {
+ Memd[d[j]+i-1] = Memd[d[n[i]]+i-1]
+ Memi[m[j]+i-1] = Memi[m[n[i]]+i-1]
+ }
+ n[i] = n[i] - 1
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+ }
+end
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icmedian.x b/pkg/images/immatch/src/imcombine/src/generic/icmedian.x
new file mode 100644
index 00000000..c482454b
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icmedian.x
@@ -0,0 +1,753 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+
+# IC_MEDIAN -- Median of lines
+
+procedure ic_medians (d, n, npts, doblank, median)
+
+pointer d[ARB] # Input data line pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+int doblank # Set blank values?
+real median[npts] # Median
+
+int i, j, k, j1, j2, n1, lo, up, lo1, up1
+bool even
+real val1, val2, val3
+short temp, wtemp
+
+include "../icombine.com"
+
+begin
+ # If no data return after possibly setting blank values.
+ if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ median[i]= blank
+ }
+ return
+ }
+
+ # If the data were previously sorted then directly compute the median.
+ if (mclip) {
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ j1 = n1 / 2 + 1
+ j2 = n1 / 2
+ even = (mod(n1,2)==0 && (medtype==MEDAVG || n1>2))
+ do i = 1, npts {
+ k = i - 1
+ if (even) {
+ val1 = Mems[d[j1]+k]
+ val2 = Mems[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Mems[d[j1]+k]
+ }
+ return
+ } else {
+ # Check for negative n values. If found then there are
+ # pixels with no good values but with values we want to
+ # use as a substitute median. In this case ignore that
+ # the good pixels have been sorted.
+ do i = 1, npts {
+ if (n[i] < 0)
+ break
+ }
+
+ if (n[i] >= 0) {
+ do i = 1, npts {
+ k = i - 1
+ n1 = n[i]
+ if (n1 > 0) {
+ j1 = n1 / 2 + 1
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1>2)) {
+ j2 = n1 / 2
+ val1 = Mems[d[j1]+k]
+ val2 = Mems[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Mems[d[j1]+k]
+ } else if (doblank == YES)
+ median[i] = blank
+ }
+ return
+ }
+ }
+ }
+
+ # Compute the median.
+ do i = 1, npts {
+ k = i - 1
+ n1 = abs(n[i])
+
+ # If there are more than 3 points use Wirth algorithm. This
+ # is the same as vops$amed.gx except for an even number of
+ # points it selects the middle two and averages.
+ if (n1 > 3) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2))
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Mems[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Mems[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Mems[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Mems[d[lo1]+k]
+ Mems[d[lo1]+k] = Mems[d[up1]+k]
+ Mems[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+
+ median[i] = Mems[d[j]+k]
+
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1 > 2)) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2)+1)
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Mems[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Mems[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Mems[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Mems[d[lo1]+k]
+ Mems[d[lo1]+k] = Mems[d[up1]+k]
+ Mems[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+ median[i] = (median[i] + Mems[d[j]+k]) / 2
+ }
+
+ # If 3 points find the median directly.
+ } else if (n1 == 3) {
+ val1 = Mems[d[1]+k]
+ val2 = Mems[d[2]+k]
+ val3 = Mems[d[3]+k]
+ if (val1 < val2) {
+ if (val2 < val3) # abc
+ median[i] = val2
+ else if (val1 < val3) # acb
+ median[i] = val3
+ else # cab
+ median[i] = val1
+ } else {
+ if (val2 > val3) # cba
+ median[i] = val2
+ else if (val1 > val3) # bca
+ median[i] = val3
+ else # bac
+ median[i] = val1
+ }
+
+ # If 2 points average.
+ } else if (n1 == 2) {
+ val1 = Mems[d[1]+k]
+ val2 = Mems[d[2]+k]
+ if (medtype == MEDAVG)
+ median[i] = (val1 + val2) / 2
+ else
+ median[i] = min (val1, val2)
+
+ # If 1 point return the value.
+ } else if (n1 == 1)
+ median[i] = Mems[d[1]+k]
+
+ # If no points return with a possibly blank value.
+ else if (doblank == YES)
+ median[i] = blank
+ }
+end
+
+# IC_MEDIAN -- Median of lines
+
+procedure ic_mediani (d, n, npts, doblank, median)
+
+pointer d[ARB] # Input data line pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+int doblank # Set blank values?
+real median[npts] # Median
+
+int i, j, k, j1, j2, n1, lo, up, lo1, up1
+bool even
+real val1, val2, val3
+int temp, wtemp
+
+include "../icombine.com"
+
+begin
+ # If no data return after possibly setting blank values.
+ if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ median[i]= blank
+ }
+ return
+ }
+
+ # If the data were previously sorted then directly compute the median.
+ if (mclip) {
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ j1 = n1 / 2 + 1
+ j2 = n1 / 2
+ even = (mod(n1,2)==0 && (medtype==MEDAVG || n1>2))
+ do i = 1, npts {
+ k = i - 1
+ if (even) {
+ val1 = Memi[d[j1]+k]
+ val2 = Memi[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Memi[d[j1]+k]
+ }
+ return
+ } else {
+ # Check for negative n values. If found then there are
+ # pixels with no good values but with values we want to
+ # use as a substitute median. In this case ignore that
+ # the good pixels have been sorted.
+ do i = 1, npts {
+ if (n[i] < 0)
+ break
+ }
+
+ if (n[i] >= 0) {
+ do i = 1, npts {
+ k = i - 1
+ n1 = n[i]
+ if (n1 > 0) {
+ j1 = n1 / 2 + 1
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1>2)) {
+ j2 = n1 / 2
+ val1 = Memi[d[j1]+k]
+ val2 = Memi[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Memi[d[j1]+k]
+ } else if (doblank == YES)
+ median[i] = blank
+ }
+ return
+ }
+ }
+ }
+
+ # Compute the median.
+ do i = 1, npts {
+ k = i - 1
+ n1 = abs(n[i])
+
+ # If there are more than 3 points use Wirth algorithm. This
+ # is the same as vops$amed.gx except for an even number of
+ # points it selects the middle two and averages.
+ if (n1 > 3) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2))
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Memi[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Memi[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Memi[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Memi[d[lo1]+k]
+ Memi[d[lo1]+k] = Memi[d[up1]+k]
+ Memi[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+
+ median[i] = Memi[d[j]+k]
+
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1 > 2)) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2)+1)
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Memi[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Memi[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Memi[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Memi[d[lo1]+k]
+ Memi[d[lo1]+k] = Memi[d[up1]+k]
+ Memi[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+ median[i] = (median[i] + Memi[d[j]+k]) / 2
+ }
+
+ # If 3 points find the median directly.
+ } else if (n1 == 3) {
+ val1 = Memi[d[1]+k]
+ val2 = Memi[d[2]+k]
+ val3 = Memi[d[3]+k]
+ if (val1 < val2) {
+ if (val2 < val3) # abc
+ median[i] = val2
+ else if (val1 < val3) # acb
+ median[i] = val3
+ else # cab
+ median[i] = val1
+ } else {
+ if (val2 > val3) # cba
+ median[i] = val2
+ else if (val1 > val3) # bca
+ median[i] = val3
+ else # bac
+ median[i] = val1
+ }
+
+ # If 2 points average.
+ } else if (n1 == 2) {
+ val1 = Memi[d[1]+k]
+ val2 = Memi[d[2]+k]
+ if (medtype == MEDAVG)
+ median[i] = (val1 + val2) / 2
+ else
+ median[i] = min (val1, val2)
+
+ # If 1 point return the value.
+ } else if (n1 == 1)
+ median[i] = Memi[d[1]+k]
+
+ # If no points return with a possibly blank value.
+ else if (doblank == YES)
+ median[i] = blank
+ }
+end
+
+# IC_MEDIAN -- Median of lines
+
+procedure ic_medianr (d, n, npts, doblank, median)
+
+pointer d[ARB] # Input data line pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+int doblank # Set blank values?
+real median[npts] # Median
+
+int i, j, k, j1, j2, n1, lo, up, lo1, up1
+bool even
+real val1, val2, val3
+real temp, wtemp
+
+include "../icombine.com"
+
+begin
+ # If no data return after possibly setting blank values.
+ if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ median[i]= blank
+ }
+ return
+ }
+
+ # If the data were previously sorted then directly compute the median.
+ if (mclip) {
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ j1 = n1 / 2 + 1
+ j2 = n1 / 2
+ even = (mod(n1,2)==0 && (medtype==MEDAVG || n1>2))
+ do i = 1, npts {
+ k = i - 1
+ if (even) {
+ val1 = Memr[d[j1]+k]
+ val2 = Memr[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Memr[d[j1]+k]
+ }
+ return
+ } else {
+ # Check for negative n values. If found then there are
+ # pixels with no good values but with values we want to
+ # use as a substitute median. In this case ignore that
+ # the good pixels have been sorted.
+ do i = 1, npts {
+ if (n[i] < 0)
+ break
+ }
+
+ if (n[i] >= 0) {
+ do i = 1, npts {
+ k = i - 1
+ n1 = n[i]
+ if (n1 > 0) {
+ j1 = n1 / 2 + 1
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1>2)) {
+ j2 = n1 / 2
+ val1 = Memr[d[j1]+k]
+ val2 = Memr[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Memr[d[j1]+k]
+ } else if (doblank == YES)
+ median[i] = blank
+ }
+ return
+ }
+ }
+ }
+
+ # Compute the median.
+ do i = 1, npts {
+ k = i - 1
+ n1 = abs(n[i])
+
+ # If there are more than 3 points use Wirth algorithm. This
+ # is the same as vops$amed.gx except for an even number of
+ # points it selects the middle two and averages.
+ if (n1 > 3) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2))
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Memr[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Memr[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Memr[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Memr[d[lo1]+k]
+ Memr[d[lo1]+k] = Memr[d[up1]+k]
+ Memr[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+
+ median[i] = Memr[d[j]+k]
+
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1 > 2)) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2)+1)
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Memr[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Memr[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Memr[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Memr[d[lo1]+k]
+ Memr[d[lo1]+k] = Memr[d[up1]+k]
+ Memr[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+ median[i] = (median[i] + Memr[d[j]+k]) / 2
+ }
+
+ # If 3 points find the median directly.
+ } else if (n1 == 3) {
+ val1 = Memr[d[1]+k]
+ val2 = Memr[d[2]+k]
+ val3 = Memr[d[3]+k]
+ if (val1 < val2) {
+ if (val2 < val3) # abc
+ median[i] = val2
+ else if (val1 < val3) # acb
+ median[i] = val3
+ else # cab
+ median[i] = val1
+ } else {
+ if (val2 > val3) # cba
+ median[i] = val2
+ else if (val1 > val3) # bca
+ median[i] = val3
+ else # bac
+ median[i] = val1
+ }
+
+ # If 2 points average.
+ } else if (n1 == 2) {
+ val1 = Memr[d[1]+k]
+ val2 = Memr[d[2]+k]
+ if (medtype == MEDAVG)
+ median[i] = (val1 + val2) / 2
+ else
+ median[i] = min (val1, val2)
+
+ # If 1 point return the value.
+ } else if (n1 == 1)
+ median[i] = Memr[d[1]+k]
+
+ # If no points return with a possibly blank value.
+ else if (doblank == YES)
+ median[i] = blank
+ }
+end
+
+# IC_MEDIAN -- Median of lines
+
+procedure ic_mediand (d, n, npts, doblank, median)
+
+pointer d[ARB] # Input data line pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+int doblank # Set blank values?
+double median[npts] # Median
+
+int i, j, k, j1, j2, n1, lo, up, lo1, up1
+bool even
+double val1, val2, val3
+double temp, wtemp
+
+include "../icombine.com"
+
+begin
+ # If no data return after possibly setting blank values.
+ if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ median[i]= blank
+ }
+ return
+ }
+
+ # If the data were previously sorted then directly compute the median.
+ if (mclip) {
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ j1 = n1 / 2 + 1
+ j2 = n1 / 2
+ even = (mod(n1,2)==0 && (medtype==MEDAVG || n1>2))
+ do i = 1, npts {
+ k = i - 1
+ if (even) {
+ val1 = Memd[d[j1]+k]
+ val2 = Memd[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Memd[d[j1]+k]
+ }
+ return
+ } else {
+ # Check for negative n values. If found then there are
+ # pixels with no good values but with values we want to
+ # use as a substitute median. In this case ignore that
+ # the good pixels have been sorted.
+ do i = 1, npts {
+ if (n[i] < 0)
+ break
+ }
+
+ if (n[i] >= 0) {
+ do i = 1, npts {
+ k = i - 1
+ n1 = n[i]
+ if (n1 > 0) {
+ j1 = n1 / 2 + 1
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1>2)) {
+ j2 = n1 / 2
+ val1 = Memd[d[j1]+k]
+ val2 = Memd[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Memd[d[j1]+k]
+ } else if (doblank == YES)
+ median[i] = blank
+ }
+ return
+ }
+ }
+ }
+
+ # Compute the median.
+ do i = 1, npts {
+ k = i - 1
+ n1 = abs(n[i])
+
+ # If there are more than 3 points use Wirth algorithm. This
+ # is the same as vops$amed.gx except for an even number of
+ # points it selects the middle two and averages.
+ if (n1 > 3) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2))
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Memd[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Memd[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Memd[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Memd[d[lo1]+k]
+ Memd[d[lo1]+k] = Memd[d[up1]+k]
+ Memd[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+
+ median[i] = Memd[d[j]+k]
+
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1 > 2)) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2)+1)
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Memd[d[j]+k]; lo1 = lo; up1 = up
+
+ repeat {
+ while (Memd[d[lo1]+k] < temp)
+ lo1 = lo1 + 1
+ while (temp < Memd[d[up1]+k])
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Memd[d[lo1]+k]
+ Memd[d[lo1]+k] = Memd[d[up1]+k]
+ Memd[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+ median[i] = (median[i] + Memd[d[j]+k]) / 2
+ }
+
+ # If 3 points find the median directly.
+ } else if (n1 == 3) {
+ val1 = Memd[d[1]+k]
+ val2 = Memd[d[2]+k]
+ val3 = Memd[d[3]+k]
+ if (val1 < val2) {
+ if (val2 < val3) # abc
+ median[i] = val2
+ else if (val1 < val3) # acb
+ median[i] = val3
+ else # cab
+ median[i] = val1
+ } else {
+ if (val2 > val3) # cba
+ median[i] = val2
+ else if (val1 > val3) # bca
+ median[i] = val3
+ else # bac
+ median[i] = val1
+ }
+
+ # If 2 points average.
+ } else if (n1 == 2) {
+ val1 = Memd[d[1]+k]
+ val2 = Memd[d[2]+k]
+ if (medtype == MEDAVG)
+ median[i] = (val1 + val2) / 2
+ else
+ median[i] = min (val1, val2)
+
+ # If 1 point return the value.
+ } else if (n1 == 1)
+ median[i] = Memd[d[1]+k]
+
+ # If no points return with a possibly blank value.
+ else if (doblank == YES)
+ median[i] = blank
+ }
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icmm.x b/pkg/images/immatch/src/imcombine/src/generic/icmm.x
new file mode 100644
index 00000000..9c8274c8
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icmm.x
@@ -0,0 +1,645 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+
+# IC_MM -- Reject a specified number of high and low pixels
+
+procedure ic_mms (d, m, n, npts)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+
+int n1, ncombine, npairs, nlow, nhigh, np
+int i, i1, j, jmax, jmin
+pointer k, kmax, kmin
+short d1, d2, dmin, dmax
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_NONE)
+ return
+
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = n1 - nlow - nhigh
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ do i = 1, npts {
+ i1 = i - 1
+ n1 = max (0, n[i])
+ if (dflag == D_MIX) {
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = max (ncombine, n1 - nlow - nhigh)
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ # Reject the npairs low and high points.
+ do np = 1, npairs {
+ k = d[1] + i1
+ d1 = Mems[k]
+ dmax = d1; dmin = d1; jmax = 1; jmin = 1; kmax = k; kmin = k
+ do j = 2, n1 {
+ d2 = d1
+ k = d[j] + i1
+ d1 = Mems[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ } else if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ j = n1 - 1
+ if (keepids) {
+ if (jmax < j) {
+ if (jmin != j) {
+ Mems[kmax] = d2
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ } else {
+ Mems[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ }
+ if (jmin < j) {
+ if (jmax != n1) {
+ Mems[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ } else {
+ Mems[kmin] = d2
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ }
+ }
+ } else {
+ if (jmax < j) {
+ if (jmin != j)
+ Mems[kmax] = d2
+ else
+ Mems[kmax] = d1
+ }
+ if (jmin < j) {
+ if (jmax != n1)
+ Mems[kmin] = d1
+ else
+ Mems[kmin] = d2
+ }
+ }
+ n1 = n1 - 2
+ }
+
+ # Reject the excess low points.
+ do np = 1, nlow {
+ k = d[1] + i1
+ d1 = Mems[k]
+ dmin = d1; jmin = 1; kmin = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Mems[k]
+ if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ if (keepids) {
+ if (jmin < n1) {
+ Mems[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmin < n1)
+ Mems[kmin] = d1
+ }
+ n1 = n1 - 1
+ }
+
+ # Reject the excess high points.
+ do np = 1, nhigh {
+ k = d[1] + i1
+ d1 = Mems[k]
+ dmax = d1; jmax = 1; kmax = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Mems[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ }
+ }
+ if (keepids) {
+ if (jmax < n1) {
+ Mems[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmax < n1)
+ Mems[kmax] = d1
+ }
+ n1 = n1 - 1
+ }
+ n[i] = n1
+ }
+
+ if (dflag == D_ALL && npairs + nlow + nhigh > 0)
+ dflag = D_MIX
+end
+
+# IC_MM -- Reject a specified number of high and low pixels
+
+procedure ic_mmi (d, m, n, npts)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+
+int n1, ncombine, npairs, nlow, nhigh, np
+int i, i1, j, jmax, jmin
+pointer k, kmax, kmin
+int d1, d2, dmin, dmax
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_NONE)
+ return
+
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = n1 - nlow - nhigh
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ do i = 1, npts {
+ i1 = i - 1
+ n1 = max (0, n[i])
+ if (dflag == D_MIX) {
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = max (ncombine, n1 - nlow - nhigh)
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ # Reject the npairs low and high points.
+ do np = 1, npairs {
+ k = d[1] + i1
+ d1 = Memi[k]
+ dmax = d1; dmin = d1; jmax = 1; jmin = 1; kmax = k; kmin = k
+ do j = 2, n1 {
+ d2 = d1
+ k = d[j] + i1
+ d1 = Memi[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ } else if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ j = n1 - 1
+ if (keepids) {
+ if (jmax < j) {
+ if (jmin != j) {
+ Memi[kmax] = d2
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ } else {
+ Memi[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ }
+ if (jmin < j) {
+ if (jmax != n1) {
+ Memi[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ } else {
+ Memi[kmin] = d2
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ }
+ }
+ } else {
+ if (jmax < j) {
+ if (jmin != j)
+ Memi[kmax] = d2
+ else
+ Memi[kmax] = d1
+ }
+ if (jmin < j) {
+ if (jmax != n1)
+ Memi[kmin] = d1
+ else
+ Memi[kmin] = d2
+ }
+ }
+ n1 = n1 - 2
+ }
+
+ # Reject the excess low points.
+ do np = 1, nlow {
+ k = d[1] + i1
+ d1 = Memi[k]
+ dmin = d1; jmin = 1; kmin = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Memi[k]
+ if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ if (keepids) {
+ if (jmin < n1) {
+ Memi[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmin < n1)
+ Memi[kmin] = d1
+ }
+ n1 = n1 - 1
+ }
+
+ # Reject the excess high points.
+ do np = 1, nhigh {
+ k = d[1] + i1
+ d1 = Memi[k]
+ dmax = d1; jmax = 1; kmax = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Memi[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ }
+ }
+ if (keepids) {
+ if (jmax < n1) {
+ Memi[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmax < n1)
+ Memi[kmax] = d1
+ }
+ n1 = n1 - 1
+ }
+ n[i] = n1
+ }
+
+ if (dflag == D_ALL && npairs + nlow + nhigh > 0)
+ dflag = D_MIX
+end
+
+# IC_MM -- Reject a specified number of high and low pixels
+
+procedure ic_mmr (d, m, n, npts)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+
+int n1, ncombine, npairs, nlow, nhigh, np
+int i, i1, j, jmax, jmin
+pointer k, kmax, kmin
+real d1, d2, dmin, dmax
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_NONE)
+ return
+
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = n1 - nlow - nhigh
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ do i = 1, npts {
+ i1 = i - 1
+ n1 = max (0, n[i])
+ if (dflag == D_MIX) {
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = max (ncombine, n1 - nlow - nhigh)
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ # Reject the npairs low and high points.
+ do np = 1, npairs {
+ k = d[1] + i1
+ d1 = Memr[k]
+ dmax = d1; dmin = d1; jmax = 1; jmin = 1; kmax = k; kmin = k
+ do j = 2, n1 {
+ d2 = d1
+ k = d[j] + i1
+ d1 = Memr[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ } else if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ j = n1 - 1
+ if (keepids) {
+ if (jmax < j) {
+ if (jmin != j) {
+ Memr[kmax] = d2
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ } else {
+ Memr[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ }
+ if (jmin < j) {
+ if (jmax != n1) {
+ Memr[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ } else {
+ Memr[kmin] = d2
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ }
+ }
+ } else {
+ if (jmax < j) {
+ if (jmin != j)
+ Memr[kmax] = d2
+ else
+ Memr[kmax] = d1
+ }
+ if (jmin < j) {
+ if (jmax != n1)
+ Memr[kmin] = d1
+ else
+ Memr[kmin] = d2
+ }
+ }
+ n1 = n1 - 2
+ }
+
+ # Reject the excess low points.
+ do np = 1, nlow {
+ k = d[1] + i1
+ d1 = Memr[k]
+ dmin = d1; jmin = 1; kmin = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Memr[k]
+ if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ if (keepids) {
+ if (jmin < n1) {
+ Memr[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmin < n1)
+ Memr[kmin] = d1
+ }
+ n1 = n1 - 1
+ }
+
+ # Reject the excess high points.
+ do np = 1, nhigh {
+ k = d[1] + i1
+ d1 = Memr[k]
+ dmax = d1; jmax = 1; kmax = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Memr[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ }
+ }
+ if (keepids) {
+ if (jmax < n1) {
+ Memr[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmax < n1)
+ Memr[kmax] = d1
+ }
+ n1 = n1 - 1
+ }
+ n[i] = n1
+ }
+
+ if (dflag == D_ALL && npairs + nlow + nhigh > 0)
+ dflag = D_MIX
+end
+
+# IC_MM -- Reject a specified number of high and low pixels
+
+procedure ic_mmd (d, m, n, npts)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+
+int n1, ncombine, npairs, nlow, nhigh, np
+int i, i1, j, jmax, jmin
+pointer k, kmax, kmin
+double d1, d2, dmin, dmax
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_NONE)
+ return
+
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = n1 - nlow - nhigh
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ do i = 1, npts {
+ i1 = i - 1
+ n1 = max (0, n[i])
+ if (dflag == D_MIX) {
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = max (ncombine, n1 - nlow - nhigh)
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ # Reject the npairs low and high points.
+ do np = 1, npairs {
+ k = d[1] + i1
+ d1 = Memd[k]
+ dmax = d1; dmin = d1; jmax = 1; jmin = 1; kmax = k; kmin = k
+ do j = 2, n1 {
+ d2 = d1
+ k = d[j] + i1
+ d1 = Memd[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ } else if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ j = n1 - 1
+ if (keepids) {
+ if (jmax < j) {
+ if (jmin != j) {
+ Memd[kmax] = d2
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ } else {
+ Memd[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ }
+ if (jmin < j) {
+ if (jmax != n1) {
+ Memd[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ } else {
+ Memd[kmin] = d2
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ }
+ }
+ } else {
+ if (jmax < j) {
+ if (jmin != j)
+ Memd[kmax] = d2
+ else
+ Memd[kmax] = d1
+ }
+ if (jmin < j) {
+ if (jmax != n1)
+ Memd[kmin] = d1
+ else
+ Memd[kmin] = d2
+ }
+ }
+ n1 = n1 - 2
+ }
+
+ # Reject the excess low points.
+ do np = 1, nlow {
+ k = d[1] + i1
+ d1 = Memd[k]
+ dmin = d1; jmin = 1; kmin = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Memd[k]
+ if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ if (keepids) {
+ if (jmin < n1) {
+ Memd[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmin < n1)
+ Memd[kmin] = d1
+ }
+ n1 = n1 - 1
+ }
+
+ # Reject the excess high points.
+ do np = 1, nhigh {
+ k = d[1] + i1
+ d1 = Memd[k]
+ dmax = d1; jmax = 1; kmax = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ d1 = Memd[k]
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ }
+ }
+ if (keepids) {
+ if (jmax < n1) {
+ Memd[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmax < n1)
+ Memd[kmax] = d1
+ }
+ n1 = n1 - 1
+ }
+ n[i] = n1
+ }
+
+ if (dflag == D_ALL && npairs + nlow + nhigh > 0)
+ dflag = D_MIX
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icnmodel.x b/pkg/images/immatch/src/imcombine/src/generic/icnmodel.x
new file mode 100644
index 00000000..559cba73
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icnmodel.x
@@ -0,0 +1,528 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+include "../icmask.h"
+
+
+# IC_NMODEL -- Compute the quadrature average (or summed) noise model.
+# Options include a weighted average/sum.
+
+procedure ic_nmodels (d, m, n, nm, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real nm[3,nimages] # Noise model parameters
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+real sum, zero
+data zero /0.0/
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Mems[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ do j = 2, n[i] {
+ val = max (zero, Mems[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Mems[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n[i] {
+ val = max (zero, Mems[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Mems[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ sumwt = wt
+ do j = 2, n1 {
+ val = max (zero, Mems[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = max (zero, Mems[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = Mems[d[1]+k]**2
+ do j = 2, n1 {
+ val = max (zero, Mems[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] +
+ (val*nm[3,j])**2
+ sum = sum + val
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Mems[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n1 {
+ val = max (zero, Mems[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_NMODEL -- Compute the quadrature average (or summed) noise model.
+# Options include a weighted average/sum.
+
+procedure ic_nmodeli (d, m, n, nm, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real nm[3,nimages] # Noise model parameters
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+real sum, zero
+data zero /0.0/
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Memi[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ do j = 2, n[i] {
+ val = max (zero, Memi[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Memi[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n[i] {
+ val = max (zero, Memi[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Memi[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ sumwt = wt
+ do j = 2, n1 {
+ val = max (zero, Memi[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = max (zero, Memi[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = Memi[d[1]+k]**2
+ do j = 2, n1 {
+ val = max (zero, Memi[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] +
+ (val*nm[3,j])**2
+ sum = sum + val
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Memi[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n1 {
+ val = max (zero, Memi[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_NMODEL -- Compute the quadrature average (or summed) noise model.
+# Options include a weighted average/sum.
+
+procedure ic_nmodelr (d, m, n, nm, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real nm[3,nimages] # Noise model parameters
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+real sum, zero
+data zero /0.0/
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Memr[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ do j = 2, n[i] {
+ val = max (zero, Memr[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Memr[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n[i] {
+ val = max (zero, Memr[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Memr[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ sumwt = wt
+ do j = 2, n1 {
+ val = max (zero, Memr[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = max (zero, Memr[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = Memr[d[1]+k]**2
+ do j = 2, n1 {
+ val = max (zero, Memr[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] +
+ (val*nm[3,j])**2
+ sum = sum + val
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Memr[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n1 {
+ val = max (zero, Memr[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_NMODEL -- Compute the quadrature average (or summed) noise model.
+# Options include a weighted average/sum.
+
+procedure ic_nmodeld (d, m, n, nm, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real nm[3,nimages] # Noise model parameters
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+double average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+double sum, zero
+data zero /0.0D0/
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Memd[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ do j = 2, n[i] {
+ val = max (zero, Memd[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Memd[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n[i] {
+ val = max (zero, Memd[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Memd[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ sumwt = wt
+ do j = 2, n1 {
+ val = max (zero, Memd[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = max (zero, Memd[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = Memd[d[1]+k]**2
+ do j = 2, n1 {
+ val = max (zero, Memd[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] +
+ (val*nm[3,j])**2
+ sum = sum + val
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Memd[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n1 {
+ val = max (zero, Memd[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icomb.x b/pkg/images/immatch/src/imcombine/src/generic/icomb.x
new file mode 100644
index 00000000..3466073b
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icomb.x
@@ -0,0 +1,2198 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include <pmset.h>
+include <error.h>
+include <syserr.h>
+include <mach.h>
+include "../icombine.h"
+
+# The following is for compiling under V2.11.
+define IM_BUFFRAC IM_BUFSIZE
+include <imset.h>
+
+
+# ICOMBINE -- Combine images
+#
+# The memory and open file descriptor limits are checked and an attempt
+# to recover is made either by setting the image pixel files to be
+# closed after I/O or by notifying the calling program that memory
+# ran out and the IMIO buffer size should be reduced. After the checks
+# a procedure for the selected combine option is called.
+# Because there may be several failure modes when reaching the file
+# limits we first assume an error is due to the file limit, except for
+# out of memory, and close some pixel files. If the error then repeats
+# on accessing the pixels the error is passed back.
+
+
+procedure icombines (in, out, scales, zeros, wts, offsets, nimages, bufsize)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real wts[nimages] # Weights
+int offsets[nimages,ARB] # Input image offsets
+int nimages # Number of input images
+int bufsize # IMIO buffer size
+
+char str[1]
+int i, j, k, npts, fd, stropen(), xt_imgnls()
+pointer sp, d, id, n, m, lflag, v, dbuf
+pointer im, buf, xt_opix(), impl1i()
+errchk stropen, xt_cpix, xt_opix, xt_imgnls, impl1i, ic_combines
+pointer impl1r()
+errchk impl1r
+
+include "../icombine.com"
+
+begin
+ npts = IM_LEN(out[1],1)
+
+ # Allocate memory.
+ call smark (sp)
+ call salloc (dbuf, nimages, TY_POINTER)
+ call salloc (d, nimages, TY_POINTER)
+ call salloc (id, nimages, TY_POINTER)
+ call salloc (n, npts, TY_INT)
+ call salloc (m, nimages, TY_POINTER)
+ call salloc (lflag, nimages, TY_INT)
+ call salloc (v, IM_MAXDIM, TY_LONG)
+ call amovki (D_ALL, Memi[lflag], nimages)
+ call amovkl (1, Meml[v], IM_MAXDIM)
+
+ # If not aligned or growing create data buffers of output length
+ # otherwise use the IMIO buffers.
+
+ if (!aligned || grow >= 1.) {
+ do i = 1, nimages {
+ call salloc (Memi[dbuf+i-1], npts, TY_SHORT)
+ call aclrs (Mems[Memi[dbuf+i-1]], npts)
+ }
+ } else {
+ do i = 1, nimages {
+ im = xt_opix (in[i], i, 1)
+ if (im != in[i]) {
+ call salloc (Memi[dbuf+i-1], npts, TY_SHORT)
+ call aclrs (Mems[Memi[dbuf+i-1]], npts)
+ }
+ }
+ call amovki (NULL, Memi[dbuf], nimages)
+ }
+
+ if (project) {
+ call imseti (in[1], IM_NBUFS, nimages)
+ call imseti (in[1], IM_BUFFRAC, 0)
+ call imseti (in[1], IM_BUFSIZE, bufsize)
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ } else {
+ # Reserve FD for string operations.
+ fd = stropen (str, 1, NEW_FILE)
+
+ # Do I/O to the images.
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ buf = impl1r (out[1])
+ call aclrr (Memr[buf], npts)
+ if (out[3] != NULL) {
+ buf = impl1r (out[3])
+ call aclrr (Memr[buf], npts)
+ }
+ if (out[2] != NULL) {
+ buf = impl1i (out[2])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[4] != NULL) {
+ buf = impl1i (out[4])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[5] != NULL) {
+ buf = impl1i (out[5])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[6] != NULL) {
+ buf = impl1i (out[6])
+ call aclri (Memi[buf], npts)
+ }
+
+ # Do I/O for first input image line.
+ if (!project) {
+ do i = 1, nimages {
+ call xt_imseti (i, "bufsize", bufsize)
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ call xt_cpix (i)
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+
+ do i = 1, nimages {
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ next
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ next
+ iferr {
+ Meml[v+1] = j
+ j = xt_imgnls (in[i], i, buf, Meml[v], 1)
+ } then {
+ call imseti (im, IM_PIXFD, NULL)
+ call sfree (sp)
+ call strclose (fd)
+ call erract (EA_ERROR)
+ }
+ }
+ }
+
+ call strclose (fd)
+ }
+
+ call ic_combines (in, out, Memi[dbuf], Memi[d], Memi[id], Memi[n],
+ Memi[m], Memi[lflag], offsets, scales, zeros, wts, nimages, npts)
+end
+
+
+# IC_COMBINE -- Combine images.
+
+procedure ic_combines (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, wts, nimages, npts)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output image
+pointer dbuf[nimages] # Data buffers for nonaligned images
+pointer d[nimages] # Data pointers
+pointer id[nimages] # Image index ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Line flags
+int offsets[nimages,ARB] # Input image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+real wts[nimages] # Combining weights
+int nimages # Number of input images
+int npts # Number of points per output line
+
+int i, ext, ctor(), errcode()
+real r, imgetr()
+pointer sp, fname, imname, v1, v2, v3, work
+pointer outdata, buf, nmod, nm, pms
+pointer immap(), impnli()
+pointer impnlr(), imgnlr()
+errchk immap, ic_scale, imgetr, ic_grow, ic_grows, ic_rmasks, ic_emask
+errchk ic_gdatas
+
+include "../icombine.com"
+data ext/0/
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (v3, IM_MAXDIM, TY_LONG)
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+
+ call ic_scale (in, out, offsets, scales, zeros, wts, nimages)
+
+ # Set combine parameters
+ switch (combine) {
+ case AVERAGE, SUM, QUAD, NMODEL:
+ if (dowts)
+ keepids = true
+ else
+ keepids = false
+ case MEDIAN:
+ dowts = false
+ keepids = false
+ }
+ docombine = true
+
+ # Get noise model parameters.
+ if (combine==NMODEL) {
+ call salloc (nmod, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nmod+3*(i-1)+2] = r
+ }
+ }
+ }
+
+ # Set rejection algorithm specific parameters
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ call salloc (nm, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nm+3*(i-1)+2] = r
+ }
+ }
+ if (!keepids) {
+ if (doscale1)
+ keepids = true
+ else {
+ do i = 2, nimages {
+ if (Memr[nm+3*(i-1)] != Memr[nm] ||
+ Memr[nm+3*(i-1)+1] != Memr[nm+1] ||
+ Memr[nm+3*(i-1)+2] != Memr[nm+2]) {
+ keepids = true
+ break
+ }
+ }
+ }
+ }
+ if (reject == CRREJECT)
+ lsigma = MAX_REAL
+ case MINMAX:
+ mclip = false
+ case PCLIP:
+ mclip = true
+ case AVSIGCLIP, SIGCLIP:
+ if (doscale1)
+ keepids = true
+ case NONE:
+ mclip = false
+ }
+
+ if (out[4] != NULL)
+ keepids = true
+
+ if (out[6] != NULL) {
+ keepids = true
+ call ic_einit (in, nimages, Memc[expkeyword], 1., 2**27-1)
+ }
+
+ if (grow >= 1.) {
+ keepids = true
+ call salloc (work, npts * nimages, TY_INT)
+ }
+ pms = NULL
+
+ if (keepids) {
+ do i = 1, nimages
+ call salloc (id[i], npts, TY_INT)
+ }
+
+# This idea turns out to has a problem with masks are used with wcs offsets.
+# the matching of masks to images based on WCS requires access to the WCS
+# of the images. For now we drop this idea but maybe a way can be identified
+# to know when this is not going to be needed.
+# # Reduce header memory use.
+# do i = 1, nimages
+# call xt_minhdr (i)
+
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatas (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ if (mclip)
+ call ic_mccdclips (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ else
+ call ic_accdclips (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ case MINMAX:
+ call ic_mms (d, id, n, npts)
+ case PCLIP:
+ call ic_pclips (d, id, n, nimages, npts, Memr[outdata])
+ case SIGCLIP:
+ if (mclip)
+ call ic_msigclips (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ else
+ call ic_asigclips (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ case AVSIGCLIP:
+ if (mclip)
+ call ic_mavsigclips (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ else
+ call ic_aavsigclips (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ }
+
+ if (pms == NULL || nkeep > 0) {
+ if (docombine) {
+ switch (combine) {
+ case AVERAGE:
+ call ic_averages (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_medians (d, n, npts, YES, Memr[outdata])
+ case SUM:
+ call ic_averages (d, id, n, wts, nimages, npts,
+ YES, NO, Memr[outdata])
+ case QUAD:
+ call ic_quads (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodels (d, id, n, Memr[nmod], wts,
+ nimages, npts, YES, YES, Memr[outdata])
+ }
+ }
+ }
+
+ if (grow >= 1.)
+ call ic_grow (out, Meml[v2], id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (pms == NULL) {
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigmas (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+ }
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ if (pms != NULL) {
+ if (nkeep > 0) {
+ call imstats (out[1], IM_IMAGENAME, Memc[fname], SZ_FNAME)
+ call imunmap (out[1])
+ iferr (buf = immap (Memc[fname], READ_WRITE, 0)) {
+ switch (errcode()) {
+ case SYS_FXFOPNOEXTNV:
+ call imgcluster (Memc[fname], Memc[fname], SZ_FNAME)
+ ext = ext + 1
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext)
+ iferr (buf = immap (Memc[imname], READ_WRITE, 0)) {
+ buf = NULL
+ ext = 0
+ }
+ repeat {
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext+1)
+ iferr (outdata = immap (Memc[imname],READ_WRITE,0))
+ break
+ if (buf != NULL)
+ call imunmap (buf)
+ buf = outdata
+ ext = ext + 1
+ }
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+ out[1] = buf
+ }
+
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatas (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ call ic_grows (Meml[v2], d, id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (nkeep > 0) {
+ do i = 1, npts {
+ if (n[i] < nkeep) {
+ Meml[v1+1] = Meml[v1+1] - 1
+ if (imgnlr (out[1], buf, Meml[v1]) == EOF)
+ ;
+ call amovr (Memr[buf], Memr[outdata], npts)
+ break
+ }
+ }
+ }
+
+ switch (combine) {
+ case AVERAGE:
+ call ic_averages (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_medians (d, n, npts, NO, Memr[outdata])
+ case SUM:
+ call ic_averages (d, id, n, wts, nimages, npts,
+ NO, NO, Memr[outdata])
+ case QUAD:
+ call ic_quads (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodels (d, id, n, Memr[nmod], wts,
+ nimages, npts, NO, YES, Memr[outdata])
+ }
+
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigmas (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ do i = 1, nimages
+ call pm_close (Memi[pms+i-1])
+ call mfree (pms, TY_POINTER)
+ }
+
+ call sfree (sp)
+end
+
+procedure icombinei (in, out, scales, zeros, wts, offsets, nimages, bufsize)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real wts[nimages] # Weights
+int offsets[nimages,ARB] # Input image offsets
+int nimages # Number of input images
+int bufsize # IMIO buffer size
+
+char str[1]
+int i, j, k, npts, fd, stropen(), xt_imgnli()
+pointer sp, d, id, n, m, lflag, v, dbuf
+pointer im, buf, xt_opix(), impl1i()
+errchk stropen, xt_cpix, xt_opix, xt_imgnli, impl1i, ic_combinei
+pointer impl1r()
+errchk impl1r
+
+include "../icombine.com"
+
+begin
+ npts = IM_LEN(out[1],1)
+
+ # Allocate memory.
+ call smark (sp)
+ call salloc (dbuf, nimages, TY_POINTER)
+ call salloc (d, nimages, TY_POINTER)
+ call salloc (id, nimages, TY_POINTER)
+ call salloc (n, npts, TY_INT)
+ call salloc (m, nimages, TY_POINTER)
+ call salloc (lflag, nimages, TY_INT)
+ call salloc (v, IM_MAXDIM, TY_LONG)
+ call amovki (D_ALL, Memi[lflag], nimages)
+ call amovkl (1, Meml[v], IM_MAXDIM)
+
+ # If not aligned or growing create data buffers of output length
+ # otherwise use the IMIO buffers.
+
+ if (!aligned || grow >= 1.) {
+ do i = 1, nimages {
+ call salloc (Memi[dbuf+i-1], npts, TY_INT)
+ call aclri (Memi[Memi[dbuf+i-1]], npts)
+ }
+ } else {
+ do i = 1, nimages {
+ im = xt_opix (in[i], i, 1)
+ if (im != in[i]) {
+ call salloc (Memi[dbuf+i-1], npts, TY_INT)
+ call aclri (Memi[Memi[dbuf+i-1]], npts)
+ }
+ }
+ call amovki (NULL, Memi[dbuf], nimages)
+ }
+
+ if (project) {
+ call imseti (in[1], IM_NBUFS, nimages)
+ call imseti (in[1], IM_BUFFRAC, 0)
+ call imseti (in[1], IM_BUFSIZE, bufsize)
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ } else {
+ # Reserve FD for string operations.
+ fd = stropen (str, 1, NEW_FILE)
+
+ # Do I/O to the images.
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ buf = impl1r (out[1])
+ call aclrr (Memr[buf], npts)
+ if (out[3] != NULL) {
+ buf = impl1r (out[3])
+ call aclrr (Memr[buf], npts)
+ }
+ if (out[2] != NULL) {
+ buf = impl1i (out[2])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[4] != NULL) {
+ buf = impl1i (out[4])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[5] != NULL) {
+ buf = impl1i (out[5])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[6] != NULL) {
+ buf = impl1i (out[6])
+ call aclri (Memi[buf], npts)
+ }
+
+ # Do I/O for first input image line.
+ if (!project) {
+ do i = 1, nimages {
+ call xt_imseti (i, "bufsize", bufsize)
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ call xt_cpix (i)
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+
+ do i = 1, nimages {
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ next
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ next
+ iferr {
+ Meml[v+1] = j
+ j = xt_imgnli (in[i], i, buf, Meml[v], 1)
+ } then {
+ call imseti (im, IM_PIXFD, NULL)
+ call sfree (sp)
+ call strclose (fd)
+ call erract (EA_ERROR)
+ }
+ }
+ }
+
+ call strclose (fd)
+ }
+
+ call ic_combinei (in, out, Memi[dbuf], Memi[d], Memi[id], Memi[n],
+ Memi[m], Memi[lflag], offsets, scales, zeros, wts, nimages, npts)
+end
+
+
+# IC_COMBINE -- Combine images.
+
+procedure ic_combinei (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, wts, nimages, npts)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output image
+pointer dbuf[nimages] # Data buffers for nonaligned images
+pointer d[nimages] # Data pointers
+pointer id[nimages] # Image index ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Line flags
+int offsets[nimages,ARB] # Input image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+real wts[nimages] # Combining weights
+int nimages # Number of input images
+int npts # Number of points per output line
+
+int i, ext, ctor(), errcode()
+real r, imgetr()
+pointer sp, fname, imname, v1, v2, v3, work
+pointer outdata, buf, nmod, nm, pms
+pointer immap(), impnli()
+pointer impnlr(), imgnlr()
+errchk immap, ic_scale, imgetr, ic_grow, ic_growi, ic_rmasks, ic_emask
+errchk ic_gdatai
+
+include "../icombine.com"
+data ext/0/
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (v3, IM_MAXDIM, TY_LONG)
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+
+ call ic_scale (in, out, offsets, scales, zeros, wts, nimages)
+
+ # Set combine parameters
+ switch (combine) {
+ case AVERAGE, SUM, QUAD, NMODEL:
+ if (dowts)
+ keepids = true
+ else
+ keepids = false
+ case MEDIAN:
+ dowts = false
+ keepids = false
+ }
+ docombine = true
+
+ # Get noise model parameters.
+ if (combine==NMODEL) {
+ call salloc (nmod, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nmod+3*(i-1)+2] = r
+ }
+ }
+ }
+
+ # Set rejection algorithm specific parameters
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ call salloc (nm, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nm+3*(i-1)+2] = r
+ }
+ }
+ if (!keepids) {
+ if (doscale1)
+ keepids = true
+ else {
+ do i = 2, nimages {
+ if (Memr[nm+3*(i-1)] != Memr[nm] ||
+ Memr[nm+3*(i-1)+1] != Memr[nm+1] ||
+ Memr[nm+3*(i-1)+2] != Memr[nm+2]) {
+ keepids = true
+ break
+ }
+ }
+ }
+ }
+ if (reject == CRREJECT)
+ lsigma = MAX_REAL
+ case MINMAX:
+ mclip = false
+ case PCLIP:
+ mclip = true
+ case AVSIGCLIP, SIGCLIP:
+ if (doscale1)
+ keepids = true
+ case NONE:
+ mclip = false
+ }
+
+ if (out[4] != NULL)
+ keepids = true
+
+ if (out[6] != NULL) {
+ keepids = true
+ call ic_einit (in, nimages, Memc[expkeyword], 1., 2**27-1)
+ }
+
+ if (grow >= 1.) {
+ keepids = true
+ call salloc (work, npts * nimages, TY_INT)
+ }
+ pms = NULL
+
+ if (keepids) {
+ do i = 1, nimages
+ call salloc (id[i], npts, TY_INT)
+ }
+
+# This idea turns out to has a problem with masks are used with wcs offsets.
+# the matching of masks to images based on WCS requires access to the WCS
+# of the images. For now we drop this idea but maybe a way can be identified
+# to know when this is not going to be needed.
+# # Reduce header memory use.
+# do i = 1, nimages
+# call xt_minhdr (i)
+
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatai (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ if (mclip)
+ call ic_mccdclipi (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ else
+ call ic_accdclipi (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ case MINMAX:
+ call ic_mmi (d, id, n, npts)
+ case PCLIP:
+ call ic_pclipi (d, id, n, nimages, npts, Memr[outdata])
+ case SIGCLIP:
+ if (mclip)
+ call ic_msigclipi (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ else
+ call ic_asigclipi (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ case AVSIGCLIP:
+ if (mclip)
+ call ic_mavsigclipi (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ else
+ call ic_aavsigclipi (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ }
+
+ if (pms == NULL || nkeep > 0) {
+ if (docombine) {
+ switch (combine) {
+ case AVERAGE:
+ call ic_averagei (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_mediani (d, n, npts, YES, Memr[outdata])
+ case SUM:
+ call ic_averagei (d, id, n, wts, nimages, npts,
+ YES, NO, Memr[outdata])
+ case QUAD:
+ call ic_quadi (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodeli (d, id, n, Memr[nmod], wts,
+ nimages, npts, YES, YES, Memr[outdata])
+ }
+ }
+ }
+
+ if (grow >= 1.)
+ call ic_grow (out, Meml[v2], id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (pms == NULL) {
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigmai (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+ }
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ if (pms != NULL) {
+ if (nkeep > 0) {
+ call imstats (out[1], IM_IMAGENAME, Memc[fname], SZ_FNAME)
+ call imunmap (out[1])
+ iferr (buf = immap (Memc[fname], READ_WRITE, 0)) {
+ switch (errcode()) {
+ case SYS_FXFOPNOEXTNV:
+ call imgcluster (Memc[fname], Memc[fname], SZ_FNAME)
+ ext = ext + 1
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext)
+ iferr (buf = immap (Memc[imname], READ_WRITE, 0)) {
+ buf = NULL
+ ext = 0
+ }
+ repeat {
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext+1)
+ iferr (outdata = immap (Memc[imname],READ_WRITE,0))
+ break
+ if (buf != NULL)
+ call imunmap (buf)
+ buf = outdata
+ ext = ext + 1
+ }
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+ out[1] = buf
+ }
+
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatai (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ call ic_growi (Meml[v2], d, id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (nkeep > 0) {
+ do i = 1, npts {
+ if (n[i] < nkeep) {
+ Meml[v1+1] = Meml[v1+1] - 1
+ if (imgnlr (out[1], buf, Meml[v1]) == EOF)
+ ;
+ call amovr (Memr[buf], Memr[outdata], npts)
+ break
+ }
+ }
+ }
+
+ switch (combine) {
+ case AVERAGE:
+ call ic_averagei (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_mediani (d, n, npts, NO, Memr[outdata])
+ case SUM:
+ call ic_averagei (d, id, n, wts, nimages, npts,
+ NO, NO, Memr[outdata])
+ case QUAD:
+ call ic_quadi (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodeli (d, id, n, Memr[nmod], wts,
+ nimages, npts, NO, YES, Memr[outdata])
+ }
+
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigmai (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ do i = 1, nimages
+ call pm_close (Memi[pms+i-1])
+ call mfree (pms, TY_POINTER)
+ }
+
+ call sfree (sp)
+end
+
+procedure icombiner (in, out, scales, zeros, wts, offsets, nimages, bufsize)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real wts[nimages] # Weights
+int offsets[nimages,ARB] # Input image offsets
+int nimages # Number of input images
+int bufsize # IMIO buffer size
+
+char str[1]
+int i, j, k, npts, fd, stropen(), xt_imgnlr()
+pointer sp, d, id, n, m, lflag, v, dbuf
+pointer im, buf, xt_opix(), impl1i()
+errchk stropen, xt_cpix, xt_opix, xt_imgnlr, impl1i, ic_combiner
+pointer impl1r()
+errchk impl1r
+
+include "../icombine.com"
+
+begin
+ npts = IM_LEN(out[1],1)
+
+ # Allocate memory.
+ call smark (sp)
+ call salloc (dbuf, nimages, TY_POINTER)
+ call salloc (d, nimages, TY_POINTER)
+ call salloc (id, nimages, TY_POINTER)
+ call salloc (n, npts, TY_INT)
+ call salloc (m, nimages, TY_POINTER)
+ call salloc (lflag, nimages, TY_INT)
+ call salloc (v, IM_MAXDIM, TY_LONG)
+ call amovki (D_ALL, Memi[lflag], nimages)
+ call amovkl (1, Meml[v], IM_MAXDIM)
+
+ # If not aligned or growing create data buffers of output length
+ # otherwise use the IMIO buffers.
+
+ if (!aligned || grow >= 1.) {
+ do i = 1, nimages {
+ call salloc (Memi[dbuf+i-1], npts, TY_REAL)
+ call aclrr (Memr[Memi[dbuf+i-1]], npts)
+ }
+ } else {
+ do i = 1, nimages {
+ im = xt_opix (in[i], i, 1)
+ if (im != in[i]) {
+ call salloc (Memi[dbuf+i-1], npts, TY_REAL)
+ call aclrr (Memr[Memi[dbuf+i-1]], npts)
+ }
+ }
+ call amovki (NULL, Memi[dbuf], nimages)
+ }
+
+ if (project) {
+ call imseti (in[1], IM_NBUFS, nimages)
+ call imseti (in[1], IM_BUFFRAC, 0)
+ call imseti (in[1], IM_BUFSIZE, bufsize)
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ } else {
+ # Reserve FD for string operations.
+ fd = stropen (str, 1, NEW_FILE)
+
+ # Do I/O to the images.
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ buf = impl1r (out[1])
+ call aclrr (Memr[buf], npts)
+ if (out[3] != NULL) {
+ buf = impl1r (out[3])
+ call aclrr (Memr[buf], npts)
+ }
+ if (out[2] != NULL) {
+ buf = impl1i (out[2])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[4] != NULL) {
+ buf = impl1i (out[4])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[5] != NULL) {
+ buf = impl1i (out[5])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[6] != NULL) {
+ buf = impl1i (out[6])
+ call aclri (Memi[buf], npts)
+ }
+
+ # Do I/O for first input image line.
+ if (!project) {
+ do i = 1, nimages {
+ call xt_imseti (i, "bufsize", bufsize)
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ call xt_cpix (i)
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+
+ do i = 1, nimages {
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ next
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ next
+ iferr {
+ Meml[v+1] = j
+ j = xt_imgnlr (in[i], i, buf, Meml[v], 1)
+ } then {
+ call imseti (im, IM_PIXFD, NULL)
+ call sfree (sp)
+ call strclose (fd)
+ call erract (EA_ERROR)
+ }
+ }
+ }
+
+ call strclose (fd)
+ }
+
+ call ic_combiner (in, out, Memi[dbuf], Memi[d], Memi[id], Memi[n],
+ Memi[m], Memi[lflag], offsets, scales, zeros, wts, nimages, npts)
+end
+
+
+# IC_COMBINE -- Combine images.
+
+procedure ic_combiner (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, wts, nimages, npts)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output image
+pointer dbuf[nimages] # Data buffers for nonaligned images
+pointer d[nimages] # Data pointers
+pointer id[nimages] # Image index ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Line flags
+int offsets[nimages,ARB] # Input image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+real wts[nimages] # Combining weights
+int nimages # Number of input images
+int npts # Number of points per output line
+
+int i, ext, ctor(), errcode()
+real r, imgetr()
+pointer sp, fname, imname, v1, v2, v3, work
+pointer outdata, buf, nmod, nm, pms
+pointer immap(), impnli()
+pointer impnlr(), imgnlr
+errchk immap, ic_scale, imgetr, ic_grow, ic_growr, ic_rmasks, ic_emask
+errchk ic_gdatar
+
+include "../icombine.com"
+data ext/0/
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (v3, IM_MAXDIM, TY_LONG)
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+
+ call ic_scale (in, out, offsets, scales, zeros, wts, nimages)
+
+ # Set combine parameters
+ switch (combine) {
+ case AVERAGE, SUM, QUAD, NMODEL:
+ if (dowts)
+ keepids = true
+ else
+ keepids = false
+ case MEDIAN:
+ dowts = false
+ keepids = false
+ }
+ docombine = true
+
+ # Get noise model parameters.
+ if (combine==NMODEL) {
+ call salloc (nmod, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nmod+3*(i-1)+2] = r
+ }
+ }
+ }
+
+ # Set rejection algorithm specific parameters
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ call salloc (nm, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nm+3*(i-1)+2] = r
+ }
+ }
+ if (!keepids) {
+ if (doscale1)
+ keepids = true
+ else {
+ do i = 2, nimages {
+ if (Memr[nm+3*(i-1)] != Memr[nm] ||
+ Memr[nm+3*(i-1)+1] != Memr[nm+1] ||
+ Memr[nm+3*(i-1)+2] != Memr[nm+2]) {
+ keepids = true
+ break
+ }
+ }
+ }
+ }
+ if (reject == CRREJECT)
+ lsigma = MAX_REAL
+ case MINMAX:
+ mclip = false
+ case PCLIP:
+ mclip = true
+ case AVSIGCLIP, SIGCLIP:
+ if (doscale1)
+ keepids = true
+ case NONE:
+ mclip = false
+ }
+
+ if (out[4] != NULL)
+ keepids = true
+
+ if (out[6] != NULL) {
+ keepids = true
+ call ic_einit (in, nimages, Memc[expkeyword], 1., 2**27-1)
+ }
+
+ if (grow >= 1.) {
+ keepids = true
+ call salloc (work, npts * nimages, TY_INT)
+ }
+ pms = NULL
+
+ if (keepids) {
+ do i = 1, nimages
+ call salloc (id[i], npts, TY_INT)
+ }
+
+# This idea turns out to has a problem with masks are used with wcs offsets.
+# the matching of masks to images based on WCS requires access to the WCS
+# of the images. For now we drop this idea but maybe a way can be identified
+# to know when this is not going to be needed.
+# # Reduce header memory use.
+# do i = 1, nimages
+# call xt_minhdr (i)
+
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatar (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ if (mclip)
+ call ic_mccdclipr (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ else
+ call ic_accdclipr (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ case MINMAX:
+ call ic_mmr (d, id, n, npts)
+ case PCLIP:
+ call ic_pclipr (d, id, n, nimages, npts, Memr[outdata])
+ case SIGCLIP:
+ if (mclip)
+ call ic_msigclipr (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ else
+ call ic_asigclipr (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ case AVSIGCLIP:
+ if (mclip)
+ call ic_mavsigclipr (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ else
+ call ic_aavsigclipr (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ }
+
+ if (pms == NULL || nkeep > 0) {
+ if (docombine) {
+ switch (combine) {
+ case AVERAGE:
+ call ic_averager (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_medianr (d, n, npts, YES, Memr[outdata])
+ case SUM:
+ call ic_averager (d, id, n, wts, nimages, npts,
+ YES, NO, Memr[outdata])
+ case QUAD:
+ call ic_quadr (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodelr (d, id, n, Memr[nmod], wts,
+ nimages, npts, YES, YES, Memr[outdata])
+ }
+ }
+ }
+
+ if (grow >= 1.)
+ call ic_grow (out, Meml[v2], id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (pms == NULL) {
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigmar (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+ }
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ if (pms != NULL) {
+ if (nkeep > 0) {
+ call imstats (out[1], IM_IMAGENAME, Memc[fname], SZ_FNAME)
+ call imunmap (out[1])
+ iferr (buf = immap (Memc[fname], READ_WRITE, 0)) {
+ switch (errcode()) {
+ case SYS_FXFOPNOEXTNV:
+ call imgcluster (Memc[fname], Memc[fname], SZ_FNAME)
+ ext = ext + 1
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext)
+ iferr (buf = immap (Memc[imname], READ_WRITE, 0)) {
+ buf = NULL
+ ext = 0
+ }
+ repeat {
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext+1)
+ iferr (outdata = immap (Memc[imname],READ_WRITE,0))
+ break
+ if (buf != NULL)
+ call imunmap (buf)
+ buf = outdata
+ ext = ext + 1
+ }
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+ out[1] = buf
+ }
+
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatar (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ call ic_growr (Meml[v2], d, id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (nkeep > 0) {
+ do i = 1, npts {
+ if (n[i] < nkeep) {
+ Meml[v1+1] = Meml[v1+1] - 1
+ if (imgnlr (out[1], buf, Meml[v1]) == EOF)
+ ;
+ call amovr (Memr[buf], Memr[outdata], npts)
+ break
+ }
+ }
+ }
+
+ switch (combine) {
+ case AVERAGE:
+ call ic_averager (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_medianr (d, n, npts, NO, Memr[outdata])
+ case SUM:
+ call ic_averager (d, id, n, wts, nimages, npts,
+ NO, NO, Memr[outdata])
+ case QUAD:
+ call ic_quadr (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodelr (d, id, n, Memr[nmod], wts,
+ nimages, npts, NO, YES, Memr[outdata])
+ }
+
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigmar (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ do i = 1, nimages
+ call pm_close (Memi[pms+i-1])
+ call mfree (pms, TY_POINTER)
+ }
+
+ call sfree (sp)
+end
+
+procedure icombined (in, out, scales, zeros, wts, offsets, nimages, bufsize)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real wts[nimages] # Weights
+int offsets[nimages,ARB] # Input image offsets
+int nimages # Number of input images
+int bufsize # IMIO buffer size
+
+char str[1]
+int i, j, k, npts, fd, stropen(), xt_imgnld()
+pointer sp, d, id, n, m, lflag, v, dbuf
+pointer im, buf, xt_opix(), impl1i()
+errchk stropen, xt_cpix, xt_opix, xt_imgnld, impl1i, ic_combined
+pointer impl1d()
+errchk impl1d
+
+include "../icombine.com"
+
+begin
+ npts = IM_LEN(out[1],1)
+
+ # Allocate memory.
+ call smark (sp)
+ call salloc (dbuf, nimages, TY_POINTER)
+ call salloc (d, nimages, TY_POINTER)
+ call salloc (id, nimages, TY_POINTER)
+ call salloc (n, npts, TY_INT)
+ call salloc (m, nimages, TY_POINTER)
+ call salloc (lflag, nimages, TY_INT)
+ call salloc (v, IM_MAXDIM, TY_LONG)
+ call amovki (D_ALL, Memi[lflag], nimages)
+ call amovkl (1, Meml[v], IM_MAXDIM)
+
+ # If not aligned or growing create data buffers of output length
+ # otherwise use the IMIO buffers.
+
+ if (!aligned || grow >= 1.) {
+ do i = 1, nimages {
+ call salloc (Memi[dbuf+i-1], npts, TY_DOUBLE)
+ call aclrd (Memd[Memi[dbuf+i-1]], npts)
+ }
+ } else {
+ do i = 1, nimages {
+ im = xt_opix (in[i], i, 1)
+ if (im != in[i]) {
+ call salloc (Memi[dbuf+i-1], npts, TY_DOUBLE)
+ call aclrd (Memd[Memi[dbuf+i-1]], npts)
+ }
+ }
+ call amovki (NULL, Memi[dbuf], nimages)
+ }
+
+ if (project) {
+ call imseti (in[1], IM_NBUFS, nimages)
+ call imseti (in[1], IM_BUFFRAC, 0)
+ call imseti (in[1], IM_BUFSIZE, bufsize)
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ } else {
+ # Reserve FD for string operations.
+ fd = stropen (str, 1, NEW_FILE)
+
+ # Do I/O to the images.
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ buf = impl1d (out[1])
+ call aclrd (Memd[buf], npts)
+ if (out[3] != NULL) {
+ buf = impl1d (out[3])
+ call aclrd (Memd[buf], npts)
+ }
+ if (out[2] != NULL) {
+ buf = impl1i (out[2])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[4] != NULL) {
+ buf = impl1i (out[4])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[5] != NULL) {
+ buf = impl1i (out[5])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[6] != NULL) {
+ buf = impl1i (out[6])
+ call aclri (Memi[buf], npts)
+ }
+
+ # Do I/O for first input image line.
+ if (!project) {
+ do i = 1, nimages {
+ call xt_imseti (i, "bufsize", bufsize)
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ call xt_cpix (i)
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+
+ do i = 1, nimages {
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ next
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ next
+ iferr {
+ Meml[v+1] = j
+ j = xt_imgnld (in[i], i, buf, Meml[v], 1)
+ } then {
+ call imseti (im, IM_PIXFD, NULL)
+ call sfree (sp)
+ call strclose (fd)
+ call erract (EA_ERROR)
+ }
+ }
+ }
+
+ call strclose (fd)
+ }
+
+ call ic_combined (in, out, Memi[dbuf], Memi[d], Memi[id], Memi[n],
+ Memi[m], Memi[lflag], offsets, scales, zeros, wts, nimages, npts)
+end
+
+
+# IC_COMBINE -- Combine images.
+
+procedure ic_combined (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, wts, nimages, npts)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output image
+pointer dbuf[nimages] # Data buffers for nonaligned images
+pointer d[nimages] # Data pointers
+pointer id[nimages] # Image index ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Line flags
+int offsets[nimages,ARB] # Input image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+real wts[nimages] # Combining weights
+int nimages # Number of input images
+int npts # Number of points per output line
+
+int i, ext, ctor(), errcode()
+real r, imgetr()
+pointer sp, fname, imname, v1, v2, v3, work
+pointer outdata, buf, nmod, nm, pms
+pointer immap(), impnli()
+pointer impnld(), imgnld
+errchk immap, ic_scale, imgetr, ic_grow, ic_growd, ic_rmasks, ic_emask
+errchk ic_gdatad
+
+include "../icombine.com"
+data ext/0/
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (v3, IM_MAXDIM, TY_LONG)
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+
+ call ic_scale (in, out, offsets, scales, zeros, wts, nimages)
+
+ # Set combine parameters
+ switch (combine) {
+ case AVERAGE, SUM, QUAD, NMODEL:
+ if (dowts)
+ keepids = true
+ else
+ keepids = false
+ case MEDIAN:
+ dowts = false
+ keepids = false
+ }
+ docombine = true
+
+ # Get noise model parameters.
+ if (combine==NMODEL) {
+ call salloc (nmod, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nmod+3*(i-1)+2] = r
+ }
+ }
+ }
+
+ # Set rejection algorithm specific parameters
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ call salloc (nm, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nm+3*(i-1)+2] = r
+ }
+ }
+ if (!keepids) {
+ if (doscale1)
+ keepids = true
+ else {
+ do i = 2, nimages {
+ if (Memr[nm+3*(i-1)] != Memr[nm] ||
+ Memr[nm+3*(i-1)+1] != Memr[nm+1] ||
+ Memr[nm+3*(i-1)+2] != Memr[nm+2]) {
+ keepids = true
+ break
+ }
+ }
+ }
+ }
+ if (reject == CRREJECT)
+ lsigma = MAX_REAL
+ case MINMAX:
+ mclip = false
+ case PCLIP:
+ mclip = true
+ case AVSIGCLIP, SIGCLIP:
+ if (doscale1)
+ keepids = true
+ case NONE:
+ mclip = false
+ }
+
+ if (out[4] != NULL)
+ keepids = true
+
+ if (out[6] != NULL) {
+ keepids = true
+ call ic_einit (in, nimages, Memc[expkeyword], 1., 2**27-1)
+ }
+
+ if (grow >= 1.) {
+ keepids = true
+ call salloc (work, npts * nimages, TY_INT)
+ }
+ pms = NULL
+
+ if (keepids) {
+ do i = 1, nimages
+ call salloc (id[i], npts, TY_INT)
+ }
+
+# This idea turns out to has a problem with masks are used with wcs offsets.
+# the matching of masks to images based on WCS requires access to the WCS
+# of the images. For now we drop this idea but maybe a way can be identified
+# to know when this is not going to be needed.
+# # Reduce header memory use.
+# do i = 1, nimages
+# call xt_minhdr (i)
+
+ while (impnld (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatad (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ if (mclip)
+ call ic_mccdclipd (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memd[outdata])
+ else
+ call ic_accdclipd (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memd[outdata])
+ case MINMAX:
+ call ic_mmd (d, id, n, npts)
+ case PCLIP:
+ call ic_pclipd (d, id, n, nimages, npts, Memd[outdata])
+ case SIGCLIP:
+ if (mclip)
+ call ic_msigclipd (d, id, n, scales, zeros, nimages, npts,
+ Memd[outdata])
+ else
+ call ic_asigclipd (d, id, n, scales, zeros, nimages, npts,
+ Memd[outdata])
+ case AVSIGCLIP:
+ if (mclip)
+ call ic_mavsigclipd (d, id, n, scales, zeros, nimages,
+ npts, Memd[outdata])
+ else
+ call ic_aavsigclipd (d, id, n, scales, zeros, nimages,
+ npts, Memd[outdata])
+ }
+
+ if (pms == NULL || nkeep > 0) {
+ if (docombine) {
+ switch (combine) {
+ case AVERAGE:
+ call ic_averaged (d, id, n, wts, nimages, npts,
+ YES, YES, Memd[outdata])
+ case MEDIAN:
+ call ic_mediand (d, n, npts, YES, Memd[outdata])
+ case SUM:
+ call ic_averaged (d, id, n, wts, nimages, npts,
+ YES, NO, Memd[outdata])
+ case QUAD:
+ call ic_quadd (d, id, n, wts, nimages, npts,
+ YES, YES, Memd[outdata])
+ case NMODEL:
+ call ic_nmodeld (d, id, n, Memr[nmod], wts,
+ nimages, npts, YES, YES, Memd[outdata])
+ }
+ }
+ }
+
+ if (grow >= 1.)
+ call ic_grow (out, Meml[v2], id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (pms == NULL) {
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnld (out[3], buf, Meml[v1])
+ call ic_sigmad (d, id, n, wts, npts, Memd[outdata],
+ Memd[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+ }
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ if (pms != NULL) {
+ if (nkeep > 0) {
+ call imstats (out[1], IM_IMAGENAME, Memc[fname], SZ_FNAME)
+ call imunmap (out[1])
+ iferr (buf = immap (Memc[fname], READ_WRITE, 0)) {
+ switch (errcode()) {
+ case SYS_FXFOPNOEXTNV:
+ call imgcluster (Memc[fname], Memc[fname], SZ_FNAME)
+ ext = ext + 1
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext)
+ iferr (buf = immap (Memc[imname], READ_WRITE, 0)) {
+ buf = NULL
+ ext = 0
+ }
+ repeat {
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext+1)
+ iferr (outdata = immap (Memc[imname],READ_WRITE,0))
+ break
+ if (buf != NULL)
+ call imunmap (buf)
+ buf = outdata
+ ext = ext + 1
+ }
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+ out[1] = buf
+ }
+
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+ while (impnld (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdatad (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ call ic_growd (Meml[v2], d, id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (nkeep > 0) {
+ do i = 1, npts {
+ if (n[i] < nkeep) {
+ Meml[v1+1] = Meml[v1+1] - 1
+ if (imgnld (out[1], buf, Meml[v1]) == EOF)
+ ;
+ call amovd (Memd[buf], Memd[outdata], npts)
+ break
+ }
+ }
+ }
+
+ switch (combine) {
+ case AVERAGE:
+ call ic_averaged (d, id, n, wts, nimages, npts,
+ NO, YES, Memd[outdata])
+ case MEDIAN:
+ call ic_mediand (d, n, npts, NO, Memd[outdata])
+ case SUM:
+ call ic_averaged (d, id, n, wts, nimages, npts,
+ NO, NO, Memd[outdata])
+ case QUAD:
+ call ic_quadd (d, id, n, wts, nimages, npts,
+ NO, YES, Memd[outdata])
+ case NMODEL:
+ call ic_nmodeld (d, id, n, Memr[nmod], wts,
+ nimages, npts, NO, YES, Memd[outdata])
+ }
+
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnld (out[3], buf, Meml[v1])
+ call ic_sigmad (d, id, n, wts, npts, Memd[outdata],
+ Memd[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+
+ do i = 1, nimages
+ call pm_close (Memi[pms+i-1])
+ call mfree (pms, TY_POINTER)
+ }
+
+ call sfree (sp)
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icpclip.x b/pkg/images/immatch/src/imcombine/src/generic/icpclip.x
new file mode 100644
index 00000000..3dfe7f48
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icpclip.x
@@ -0,0 +1,879 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 3 # Minimum number for clipping
+
+
+# IC_PCLIP -- Percentile clip
+#
+# 1) Find the median
+# 2) Find the pixel which is the specified order index away
+# 3) Use the data value difference as a sigma and apply clipping
+# 4) Since the median is known return it so it does not have to be recomputed
+
+procedure ic_pclips (d, m, n, nimages, npts, median)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[npts] # Number of good pixels
+int nimages # Number of input images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, n4, n5, nl, nh, nin, maxkeep
+bool even, fp_equalr()
+real sigma, r, s, t
+pointer sp, resid, mp1, mp2
+real med
+
+include "../icombine.com"
+
+begin
+ # There must be at least MINCLIP and more than nkeep pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Set sign of pclip parameter
+ if (pclip < 0)
+ t = -1.
+ else
+ t = 1.
+
+ # If there are no rejected pixels compute certain parameters once.
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0.) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ nin = n1
+ }
+
+ # Now apply clipping.
+ do i = 1, npts {
+ # Compute median.
+ if (dflag == D_MIX) {
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 == 0) {
+ if (combine == MEDIAN)
+ median[i] = blank
+ next
+ }
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ }
+
+ j = i - 1
+ if (even) {
+ med = Mems[d[n2-1]+j]
+ med = (med + Mems[d[n2]+j]) / 2.
+ } else
+ med = Mems[d[n2]+j]
+
+ if (n1 < max (MINCLIP, maxkeep+1)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Define sigma for clipping
+ sigma = t * (Mems[d[n3]+j] - med)
+ if (fp_equalr (sigma, 0.)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Reject pixels and save residuals.
+ # Check if any pixels are clipped.
+ # If so recompute the median and reset the number of good pixels.
+ # Only reorder if needed.
+
+ for (nl=1; nl<=n1; nl=nl+1) {
+ r = (med - Mems[d[nl]+j]) / sigma
+ if (r < lsigma)
+ break
+ Memr[resid+nl] = r
+ }
+ for (nh=n1; nh>=1; nh=nh-1) {
+ r = (Mems[d[nh]+j] - med) / sigma
+ if (r < hsigma)
+ break
+ Memr[resid+nh] = r
+ }
+ n4 = nh - nl + 1
+
+ # If too many pixels are rejected add some back in.
+ # All pixels with the same residual are added.
+ while (n4 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n4 = nh - nl + 1
+ }
+
+ # If any pixels are rejected recompute the median.
+ if (nl > 1 || nh < n1) {
+ n5 = nl + n4 / 2
+ if (mod (n4, 2) == 0) {
+ med = Mems[d[n5-1]+j]
+ med = (med + Mems[d[n5]+j]) / 2.
+ } else
+ med = Mems[d[n5]+j]
+ n[i] = n4
+ }
+ if (combine == MEDIAN)
+ median[i] = med
+
+ # Reorder if pixels only if necessary.
+ if (nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ k = max (nl, n4 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mems[d[l]+j] = Mems[d[k]+j]
+ if (grow >= 1.) {
+ mp1 = m[l] + j
+ mp2 = m[k] + j
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+j] = Memi[m[k]+j]
+ k = k + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mems[d[l]+j] = Mems[d[k]+j]
+ k = k + 1
+ }
+ }
+ }
+ }
+
+ # Check if data flag needs to be reset for rejected pixels.
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag whether the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_PCLIP -- Percentile clip
+#
+# 1) Find the median
+# 2) Find the pixel which is the specified order index away
+# 3) Use the data value difference as a sigma and apply clipping
+# 4) Since the median is known return it so it does not have to be recomputed
+
+procedure ic_pclipi (d, m, n, nimages, npts, median)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[npts] # Number of good pixels
+int nimages # Number of input images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, n4, n5, nl, nh, nin, maxkeep
+bool even, fp_equalr()
+real sigma, r, s, t
+pointer sp, resid, mp1, mp2
+real med
+
+include "../icombine.com"
+
+begin
+ # There must be at least MINCLIP and more than nkeep pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Set sign of pclip parameter
+ if (pclip < 0)
+ t = -1.
+ else
+ t = 1.
+
+ # If there are no rejected pixels compute certain parameters once.
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0.) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ nin = n1
+ }
+
+ # Now apply clipping.
+ do i = 1, npts {
+ # Compute median.
+ if (dflag == D_MIX) {
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 == 0) {
+ if (combine == MEDIAN)
+ median[i] = blank
+ next
+ }
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ }
+
+ j = i - 1
+ if (even) {
+ med = Memi[d[n2-1]+j]
+ med = (med + Memi[d[n2]+j]) / 2.
+ } else
+ med = Memi[d[n2]+j]
+
+ if (n1 < max (MINCLIP, maxkeep+1)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Define sigma for clipping
+ sigma = t * (Memi[d[n3]+j] - med)
+ if (fp_equalr (sigma, 0.)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Reject pixels and save residuals.
+ # Check if any pixels are clipped.
+ # If so recompute the median and reset the number of good pixels.
+ # Only reorder if needed.
+
+ for (nl=1; nl<=n1; nl=nl+1) {
+ r = (med - Memi[d[nl]+j]) / sigma
+ if (r < lsigma)
+ break
+ Memr[resid+nl] = r
+ }
+ for (nh=n1; nh>=1; nh=nh-1) {
+ r = (Memi[d[nh]+j] - med) / sigma
+ if (r < hsigma)
+ break
+ Memr[resid+nh] = r
+ }
+ n4 = nh - nl + 1
+
+ # If too many pixels are rejected add some back in.
+ # All pixels with the same residual are added.
+ while (n4 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n4 = nh - nl + 1
+ }
+
+ # If any pixels are rejected recompute the median.
+ if (nl > 1 || nh < n1) {
+ n5 = nl + n4 / 2
+ if (mod (n4, 2) == 0) {
+ med = Memi[d[n5-1]+j]
+ med = (med + Memi[d[n5]+j]) / 2.
+ } else
+ med = Memi[d[n5]+j]
+ n[i] = n4
+ }
+ if (combine == MEDIAN)
+ median[i] = med
+
+ # Reorder if pixels only if necessary.
+ if (nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ k = max (nl, n4 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memi[d[l]+j] = Memi[d[k]+j]
+ if (grow >= 1.) {
+ mp1 = m[l] + j
+ mp2 = m[k] + j
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+j] = Memi[m[k]+j]
+ k = k + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memi[d[l]+j] = Memi[d[k]+j]
+ k = k + 1
+ }
+ }
+ }
+ }
+
+ # Check if data flag needs to be reset for rejected pixels.
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag whether the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_PCLIP -- Percentile clip
+#
+# 1) Find the median
+# 2) Find the pixel which is the specified order index away
+# 3) Use the data value difference as a sigma and apply clipping
+# 4) Since the median is known return it so it does not have to be recomputed
+
+procedure ic_pclipr (d, m, n, nimages, npts, median)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[npts] # Number of good pixels
+int nimages # Number of input images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, n4, n5, nl, nh, nin, maxkeep
+bool even, fp_equalr()
+real sigma, r, s, t
+pointer sp, resid, mp1, mp2
+real med
+
+include "../icombine.com"
+
+begin
+ # There must be at least MINCLIP and more than nkeep pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Set sign of pclip parameter
+ if (pclip < 0)
+ t = -1.
+ else
+ t = 1.
+
+ # If there are no rejected pixels compute certain parameters once.
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0.) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ nin = n1
+ }
+
+ # Now apply clipping.
+ do i = 1, npts {
+ # Compute median.
+ if (dflag == D_MIX) {
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 == 0) {
+ if (combine == MEDIAN)
+ median[i] = blank
+ next
+ }
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ }
+
+ j = i - 1
+ if (even) {
+ med = Memr[d[n2-1]+j]
+ med = (med + Memr[d[n2]+j]) / 2.
+ } else
+ med = Memr[d[n2]+j]
+
+ if (n1 < max (MINCLIP, maxkeep+1)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Define sigma for clipping
+ sigma = t * (Memr[d[n3]+j] - med)
+ if (fp_equalr (sigma, 0.)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Reject pixels and save residuals.
+ # Check if any pixels are clipped.
+ # If so recompute the median and reset the number of good pixels.
+ # Only reorder if needed.
+
+ for (nl=1; nl<=n1; nl=nl+1) {
+ r = (med - Memr[d[nl]+j]) / sigma
+ if (r < lsigma)
+ break
+ Memr[resid+nl] = r
+ }
+ for (nh=n1; nh>=1; nh=nh-1) {
+ r = (Memr[d[nh]+j] - med) / sigma
+ if (r < hsigma)
+ break
+ Memr[resid+nh] = r
+ }
+ n4 = nh - nl + 1
+
+ # If too many pixels are rejected add some back in.
+ # All pixels with the same residual are added.
+ while (n4 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n4 = nh - nl + 1
+ }
+
+ # If any pixels are rejected recompute the median.
+ if (nl > 1 || nh < n1) {
+ n5 = nl + n4 / 2
+ if (mod (n4, 2) == 0) {
+ med = Memr[d[n5-1]+j]
+ med = (med + Memr[d[n5]+j]) / 2.
+ } else
+ med = Memr[d[n5]+j]
+ n[i] = n4
+ }
+ if (combine == MEDIAN)
+ median[i] = med
+
+ # Reorder if pixels only if necessary.
+ if (nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ k = max (nl, n4 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memr[d[l]+j] = Memr[d[k]+j]
+ if (grow >= 1.) {
+ mp1 = m[l] + j
+ mp2 = m[k] + j
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+j] = Memi[m[k]+j]
+ k = k + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memr[d[l]+j] = Memr[d[k]+j]
+ k = k + 1
+ }
+ }
+ }
+ }
+
+ # Check if data flag needs to be reset for rejected pixels.
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag whether the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_PCLIP -- Percentile clip
+#
+# 1) Find the median
+# 2) Find the pixel which is the specified order index away
+# 3) Use the data value difference as a sigma and apply clipping
+# 4) Since the median is known return it so it does not have to be recomputed
+
+procedure ic_pclipd (d, m, n, nimages, npts, median)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[npts] # Number of good pixels
+int nimages # Number of input images
+int npts # Number of output points per line
+double median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, n4, n5, nl, nh, nin, maxkeep
+bool even, fp_equalr()
+real sigma, r, s, t
+pointer sp, resid, mp1, mp2
+double med
+
+include "../icombine.com"
+
+begin
+ # There must be at least MINCLIP and more than nkeep pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Set sign of pclip parameter
+ if (pclip < 0)
+ t = -1.
+ else
+ t = 1.
+
+ # If there are no rejected pixels compute certain parameters once.
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0.) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ nin = n1
+ }
+
+ # Now apply clipping.
+ do i = 1, npts {
+ # Compute median.
+ if (dflag == D_MIX) {
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 == 0) {
+ if (combine == MEDIAN)
+ median[i] = blank
+ next
+ }
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ }
+
+ j = i - 1
+ if (even) {
+ med = Memd[d[n2-1]+j]
+ med = (med + Memd[d[n2]+j]) / 2.
+ } else
+ med = Memd[d[n2]+j]
+
+ if (n1 < max (MINCLIP, maxkeep+1)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Define sigma for clipping
+ sigma = t * (Memd[d[n3]+j] - med)
+ if (fp_equalr (sigma, 0.)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Reject pixels and save residuals.
+ # Check if any pixels are clipped.
+ # If so recompute the median and reset the number of good pixels.
+ # Only reorder if needed.
+
+ for (nl=1; nl<=n1; nl=nl+1) {
+ r = (med - Memd[d[nl]+j]) / sigma
+ if (r < lsigma)
+ break
+ Memr[resid+nl] = r
+ }
+ for (nh=n1; nh>=1; nh=nh-1) {
+ r = (Memd[d[nh]+j] - med) / sigma
+ if (r < hsigma)
+ break
+ Memr[resid+nh] = r
+ }
+ n4 = nh - nl + 1
+
+ # If too many pixels are rejected add some back in.
+ # All pixels with the same residual are added.
+ while (n4 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n4 = nh - nl + 1
+ }
+
+ # If any pixels are rejected recompute the median.
+ if (nl > 1 || nh < n1) {
+ n5 = nl + n4 / 2
+ if (mod (n4, 2) == 0) {
+ med = Memd[d[n5-1]+j]
+ med = (med + Memd[d[n5]+j]) / 2.
+ } else
+ med = Memd[d[n5]+j]
+ n[i] = n4
+ }
+ if (combine == MEDIAN)
+ median[i] = med
+
+ # Reorder if pixels only if necessary.
+ if (nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ k = max (nl, n4 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memd[d[l]+j] = Memd[d[k]+j]
+ if (grow >= 1.) {
+ mp1 = m[l] + j
+ mp2 = m[k] + j
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+j] = Memi[m[k]+j]
+ k = k + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memd[d[l]+j] = Memd[d[k]+j]
+ k = k + 1
+ }
+ }
+ }
+ }
+
+ # Check if data flag needs to be reset for rejected pixels.
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag whether the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icquad.x b/pkg/images/immatch/src/imcombine/src/generic/icquad.x
new file mode 100644
index 00000000..4ba5eb14
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icquad.x
@@ -0,0 +1,476 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+include "../icmask.h"
+
+
+# IC_QUAD -- Compute the quadrature average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_quads (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+real sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = Mems[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ do j = 2, n[i] {
+ val = Mems[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val * wt) ** 2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = Mems[d[1]+k]
+ sum = val**2
+ do j = 2, n[i] {
+ val = Mems[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Mems[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ sumwt = wt
+ do j = 2, n1 {
+ val = Mems[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val* wt) ** 2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = Mems[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Mems[d[j]+k]
+ sum = sum + val**2
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Mems[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Mems[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_QUAD -- Compute the quadrature average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_quadi (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+real sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = Memi[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ do j = 2, n[i] {
+ val = Memi[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val * wt) ** 2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = Memi[d[1]+k]
+ sum = val**2
+ do j = 2, n[i] {
+ val = Memi[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Memi[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ sumwt = wt
+ do j = 2, n1 {
+ val = Memi[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val* wt) ** 2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = Memi[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Memi[d[j]+k]
+ sum = sum + val**2
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Memi[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Memi[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_QUAD -- Compute the quadrature average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_quadr (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+real average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+real sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = Memr[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ do j = 2, n[i] {
+ val = Memr[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val * wt) ** 2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = Memr[d[1]+k]
+ sum = val**2
+ do j = 2, n[i] {
+ val = Memr[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Memr[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ sumwt = wt
+ do j = 2, n1 {
+ val = Memr[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val* wt) ** 2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = Memr[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Memr[d[j]+k]
+ sum = sum + val**2
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Memr[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Memr[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
+# IC_QUAD -- Compute the quadrature average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_quadd (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+double average[npts] # Average (returned)
+
+int i, j, k, n1
+real val, wt, sumwt
+double sum
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = Memd[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ do j = 2, n[i] {
+ val = Memd[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val * wt) ** 2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = Memd[d[1]+k]
+ sum = val**2
+ do j = 2, n[i] {
+ val = Memd[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Memd[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ sumwt = wt
+ do j = 2, n1 {
+ val = Memd[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val* wt) ** 2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = Memd[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Memd[d[j]+k]
+ sum = sum + val**2
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Memd[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Memd[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icsclip.x b/pkg/images/immatch/src/imcombine/src/generic/icsclip.x
new file mode 100644
index 00000000..2f2ac17e
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icsclip.x
@@ -0,0 +1,1923 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 3 # Mininum number of images for algorithm
+
+
+# IC_ASIGCLIP -- Reject pixels using sigma clipping about the average
+# The initial average rejects the high and low pixels. A correction for
+# different scalings of the images may be made. Weights are not used.
+
+procedure ic_asigclips (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, r, one
+data one /1.0/
+pointer sp, resid, w, wp, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Flag whether returned average needs to be recomputed.
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Save the residuals and the sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Do sigma clipping.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+
+ # If there are not enough pixels simply compute the average.
+ if (n1 < max (3, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Mems[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mems[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ # Compute average with the high and low rejected.
+ low = Mems[d[1]+k]
+ high = Mems[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Mems[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Iteratively reject pixels and compute the final average if needed.
+ # Compact the data and keep track of the image IDs if needed.
+
+ repeat {
+ n2 = n1
+ if (doscale1) {
+ # Compute sigma corrected for scaling.
+ s = 0.
+ wp = w - 1
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Mems[dp1]
+ l = Memi[mp1]
+ r = sqrt (max (one, (a + zeros[l]) / scales[l]))
+ s = s + ((d1 - a) / r) ** 2
+ Memr[wp] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ wp = w - 1
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Mems[dp1]
+ r = (d1 - a) / (s * Memr[wp])
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ Memr[wp] = Memr[w+n1-1]
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } else {
+ # Compute the sigma without scale correction.
+ s = 0.
+ do j = 1, n1
+ s = s + (Mems[d[j]+k] - a) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Mems[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mems[dp1]
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Mems[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mems[dp1]
+ Mems[dp1] = Mems[dp2]
+ Mems[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Mems[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MSIGCLIP -- Reject pixels using sigma clipping about the median
+
+procedure ic_msigclips (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, w, mp1, mp2
+real med, one
+data one /1.0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Save the residuals and sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0)
+ med = (Mems[d[n3-1]+k] + Mems[d[n3]+k]) / 2.
+ else
+ med = Mems[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ # Compute the sigma with scaling correction.
+ s = 0.
+ do j = nl, nh {
+ l = Memi[m[j]+k]
+ r = sqrt (max (one, (med + zeros[l]) / scales[l]))
+ s = s + ((Mems[d[j]+k] - med) / r) ** 2
+ Memr[w+j-1] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Mems[d[nl]+k]) / (s * Memr[w+nl-1])
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Mems[d[nh]+k] - med) / (s * Memr[w+nh-1])
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ # Compute the sigma without scaling correction.
+ s = 0.
+ do j = nl, nh
+ s = s + (Mems[d[j]+k] - med) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Mems[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Mems[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mems[d[l]+k] = Mems[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mems[d[l]+k] = Mems[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_ASIGCLIP -- Reject pixels using sigma clipping about the average
+# The initial average rejects the high and low pixels. A correction for
+# different scalings of the images may be made. Weights are not used.
+
+procedure ic_asigclipi (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, r, one
+data one /1.0/
+pointer sp, resid, w, wp, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Flag whether returned average needs to be recomputed.
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Save the residuals and the sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Do sigma clipping.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+
+ # If there are not enough pixels simply compute the average.
+ if (n1 < max (3, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memi[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memi[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ # Compute average with the high and low rejected.
+ low = Memi[d[1]+k]
+ high = Memi[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memi[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Iteratively reject pixels and compute the final average if needed.
+ # Compact the data and keep track of the image IDs if needed.
+
+ repeat {
+ n2 = n1
+ if (doscale1) {
+ # Compute sigma corrected for scaling.
+ s = 0.
+ wp = w - 1
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Memi[dp1]
+ l = Memi[mp1]
+ r = sqrt (max (one, (a + zeros[l]) / scales[l]))
+ s = s + ((d1 - a) / r) ** 2
+ Memr[wp] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ wp = w - 1
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Memi[dp1]
+ r = (d1 - a) / (s * Memr[wp])
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ Memr[wp] = Memr[w+n1-1]
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } else {
+ # Compute the sigma without scale correction.
+ s = 0.
+ do j = 1, n1
+ s = s + (Memi[d[j]+k] - a) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Memi[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memi[dp1]
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memi[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memi[dp1]
+ Memi[dp1] = Memi[dp2]
+ Memi[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memi[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MSIGCLIP -- Reject pixels using sigma clipping about the median
+
+procedure ic_msigclipi (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, w, mp1, mp2
+real med, one
+data one /1.0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Save the residuals and sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0)
+ med = (Memi[d[n3-1]+k] + Memi[d[n3]+k]) / 2.
+ else
+ med = Memi[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ # Compute the sigma with scaling correction.
+ s = 0.
+ do j = nl, nh {
+ l = Memi[m[j]+k]
+ r = sqrt (max (one, (med + zeros[l]) / scales[l]))
+ s = s + ((Memi[d[j]+k] - med) / r) ** 2
+ Memr[w+j-1] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memi[d[nl]+k]) / (s * Memr[w+nl-1])
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memi[d[nh]+k] - med) / (s * Memr[w+nh-1])
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ # Compute the sigma without scaling correction.
+ s = 0.
+ do j = nl, nh
+ s = s + (Memi[d[j]+k] - med) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memi[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memi[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memi[d[l]+k] = Memi[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memi[d[l]+k] = Memi[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_ASIGCLIP -- Reject pixels using sigma clipping about the average
+# The initial average rejects the high and low pixels. A correction for
+# different scalings of the images may be made. Weights are not used.
+
+procedure ic_asigclipr (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+real d1, low, high, sum, a, s, r, one
+data one /1.0/
+pointer sp, resid, w, wp, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Flag whether returned average needs to be recomputed.
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Save the residuals and the sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Do sigma clipping.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+
+ # If there are not enough pixels simply compute the average.
+ if (n1 < max (3, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memr[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memr[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ # Compute average with the high and low rejected.
+ low = Memr[d[1]+k]
+ high = Memr[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memr[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Iteratively reject pixels and compute the final average if needed.
+ # Compact the data and keep track of the image IDs if needed.
+
+ repeat {
+ n2 = n1
+ if (doscale1) {
+ # Compute sigma corrected for scaling.
+ s = 0.
+ wp = w - 1
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Memr[dp1]
+ l = Memi[mp1]
+ r = sqrt (max (one, (a + zeros[l]) / scales[l]))
+ s = s + ((d1 - a) / r) ** 2
+ Memr[wp] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ wp = w - 1
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Memr[dp1]
+ r = (d1 - a) / (s * Memr[wp])
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ Memr[wp] = Memr[w+n1-1]
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } else {
+ # Compute the sigma without scale correction.
+ s = 0.
+ do j = 1, n1
+ s = s + (Memr[d[j]+k] - a) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Memr[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memr[dp1]
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memr[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memr[dp1]
+ Memr[dp1] = Memr[dp2]
+ Memr[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memr[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MSIGCLIP -- Reject pixels using sigma clipping about the median
+
+procedure ic_msigclipr (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+real median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, w, mp1, mp2
+real med, one
+data one /1.0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Save the residuals and sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0)
+ med = (Memr[d[n3-1]+k] + Memr[d[n3]+k]) / 2.
+ else
+ med = Memr[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ # Compute the sigma with scaling correction.
+ s = 0.
+ do j = nl, nh {
+ l = Memi[m[j]+k]
+ r = sqrt (max (one, (med + zeros[l]) / scales[l]))
+ s = s + ((Memr[d[j]+k] - med) / r) ** 2
+ Memr[w+j-1] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memr[d[nl]+k]) / (s * Memr[w+nl-1])
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memr[d[nh]+k] - med) / (s * Memr[w+nh-1])
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ # Compute the sigma without scaling correction.
+ s = 0.
+ do j = nl, nh
+ s = s + (Memr[d[j]+k] - med) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memr[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memr[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memr[d[l]+k] = Memr[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memr[d[l]+k] = Memr[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
+# IC_ASIGCLIP -- Reject pixels using sigma clipping about the average
+# The initial average rejects the high and low pixels. A correction for
+# different scalings of the images may be made. Weights are not used.
+
+procedure ic_asigclipd (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+double average[npts] # Average
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+double d1, low, high, sum, a, s, r, one
+data one /1.0D0/
+pointer sp, resid, w, wp, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Flag whether returned average needs to be recomputed.
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Save the residuals and the sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Do sigma clipping.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+
+ # If there are not enough pixels simply compute the average.
+ if (n1 < max (3, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Memd[d[1]+k]
+ do j = 2, n1
+ sum = sum + Memd[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ # Compute average with the high and low rejected.
+ low = Memd[d[1]+k]
+ high = Memd[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Memd[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Iteratively reject pixels and compute the final average if needed.
+ # Compact the data and keep track of the image IDs if needed.
+
+ repeat {
+ n2 = n1
+ if (doscale1) {
+ # Compute sigma corrected for scaling.
+ s = 0.
+ wp = w - 1
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Memd[dp1]
+ l = Memi[mp1]
+ r = sqrt (max (one, (a + zeros[l]) / scales[l]))
+ s = s + ((d1 - a) / r) ** 2
+ Memr[wp] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ wp = w - 1
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Memd[dp1]
+ r = (d1 - a) / (s * Memr[wp])
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ Memr[wp] = Memr[w+n1-1]
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } else {
+ # Compute the sigma without scale correction.
+ s = 0.
+ do j = 1, n1
+ s = s + (Memd[d[j]+k] - a) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Memd[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memd[dp1]
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Memd[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Memd[dp1]
+ Memd[dp1] = Memd[dp2]
+ Memd[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Memd[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MSIGCLIP -- Reject pixels using sigma clipping about the median
+
+procedure ic_msigclipd (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+double median[npts] # Median
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, w, mp1, mp2
+double med, one
+data one /1.0D0/
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Save the residuals and sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0)
+ med = (Memd[d[n3-1]+k] + Memd[d[n3]+k]) / 2.
+ else
+ med = Memd[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ # Compute the sigma with scaling correction.
+ s = 0.
+ do j = nl, nh {
+ l = Memi[m[j]+k]
+ r = sqrt (max (one, (med + zeros[l]) / scales[l]))
+ s = s + ((Memd[d[j]+k] - med) / r) ** 2
+ Memr[w+j-1] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memd[d[nl]+k]) / (s * Memr[w+nl-1])
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memd[d[nh]+k] - med) / (s * Memr[w+nh-1])
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ # Compute the sigma without scaling correction.
+ s = 0.
+ do j = nl, nh
+ s = s + (Memd[d[j]+k] - med) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Memd[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Memd[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Memd[d[l]+k] = Memd[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Memd[d[l]+k] = Memd[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icsigma.x b/pkg/images/immatch/src/imcombine/src/generic/icsigma.x
new file mode 100644
index 00000000..b9c9a781
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icsigma.x
@@ -0,0 +1,434 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include "../icombine.h"
+
+
+# IC_SIGMA -- Compute the sigma image line.
+# The estimated sigma includes a correction for the finite population.
+# Weights are used if desired.
+
+procedure ic_sigmas (d, m, n, wts, npts, average, sigma)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of points
+real wts[ARB] # Weights
+int npts # Number of output points per line
+real average[npts] # Average
+real sigma[npts] # Sigma line (returned)
+
+int i, j, k, n1
+real wt, sigcor, sumwt
+real a, sum
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ if (dowts) {
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Mems[d[1]+k] - a) ** 2 * wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Mems[d[j]+k] - a) ** 2 * wt
+ }
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ } else {
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ sum = (Mems[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Mems[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ do i = 1, npts
+ sigma[i] = blank
+ } else {
+ if (dowts) {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 -1)
+ else
+ sigcor = 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Mems[d[1]+k] - a) ** 2 * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Mems[d[j]+k] - a) ** 2 * wt
+ sumwt = sumwt + wt
+ }
+ if (sumwt > 0)
+ sigma[i] = sqrt (sum / sumwt * sigcor)
+ else {
+ sum = (Mems[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Mems[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum / n1 * sigcor)
+ }
+ } else
+ sigma[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ a = average[i]
+ sum = (Mems[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Mems[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ } else
+ sigma[i] = blank
+ }
+ }
+ }
+end
+
+# IC_SIGMA -- Compute the sigma image line.
+# The estimated sigma includes a correction for the finite population.
+# Weights are used if desired.
+
+procedure ic_sigmai (d, m, n, wts, npts, average, sigma)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of points
+real wts[ARB] # Weights
+int npts # Number of output points per line
+real average[npts] # Average
+real sigma[npts] # Sigma line (returned)
+
+int i, j, k, n1
+real wt, sigcor, sumwt
+real a, sum
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ if (dowts) {
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Memi[d[1]+k] - a) ** 2 * wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Memi[d[j]+k] - a) ** 2 * wt
+ }
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ } else {
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ sum = (Memi[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memi[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ do i = 1, npts
+ sigma[i] = blank
+ } else {
+ if (dowts) {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 -1)
+ else
+ sigcor = 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Memi[d[1]+k] - a) ** 2 * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Memi[d[j]+k] - a) ** 2 * wt
+ sumwt = sumwt + wt
+ }
+ if (sumwt > 0)
+ sigma[i] = sqrt (sum / sumwt * sigcor)
+ else {
+ sum = (Memi[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memi[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum / n1 * sigcor)
+ }
+ } else
+ sigma[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ a = average[i]
+ sum = (Memi[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memi[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ } else
+ sigma[i] = blank
+ }
+ }
+ }
+end
+
+# IC_SIGMA -- Compute the sigma image line.
+# The estimated sigma includes a correction for the finite population.
+# Weights are used if desired.
+
+procedure ic_sigmar (d, m, n, wts, npts, average, sigma)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of points
+real wts[ARB] # Weights
+int npts # Number of output points per line
+real average[npts] # Average
+real sigma[npts] # Sigma line (returned)
+
+int i, j, k, n1
+real wt, sigcor, sumwt
+real a, sum
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ if (dowts) {
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Memr[d[1]+k] - a) ** 2 * wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Memr[d[j]+k] - a) ** 2 * wt
+ }
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ } else {
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ sum = (Memr[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memr[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ do i = 1, npts
+ sigma[i] = blank
+ } else {
+ if (dowts) {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 -1)
+ else
+ sigcor = 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Memr[d[1]+k] - a) ** 2 * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Memr[d[j]+k] - a) ** 2 * wt
+ sumwt = sumwt + wt
+ }
+ if (sumwt > 0)
+ sigma[i] = sqrt (sum / sumwt * sigcor)
+ else {
+ sum = (Memr[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memr[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum / n1 * sigcor)
+ }
+ } else
+ sigma[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ a = average[i]
+ sum = (Memr[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memr[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ } else
+ sigma[i] = blank
+ }
+ }
+ }
+end
+
+# IC_SIGMA -- Compute the sigma image line.
+# The estimated sigma includes a correction for the finite population.
+# Weights are used if desired.
+
+procedure ic_sigmad (d, m, n, wts, npts, average, sigma)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of points
+real wts[ARB] # Weights
+int npts # Number of output points per line
+double average[npts] # Average
+double sigma[npts] # Sigma line (returned)
+
+int i, j, k, n1
+real wt, sigcor, sumwt
+double a, sum
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ if (dowts) {
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Memd[d[1]+k] - a) ** 2 * wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Memd[d[j]+k] - a) ** 2 * wt
+ }
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ } else {
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ sum = (Memd[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memd[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ do i = 1, npts
+ sigma[i] = blank
+ } else {
+ if (dowts) {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 -1)
+ else
+ sigcor = 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Memd[d[1]+k] - a) ** 2 * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Memd[d[j]+k] - a) ** 2 * wt
+ sumwt = sumwt + wt
+ }
+ if (sumwt > 0)
+ sigma[i] = sqrt (sum / sumwt * sigcor)
+ else {
+ sum = (Memd[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memd[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum / n1 * sigcor)
+ }
+ } else
+ sigma[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ a = average[i]
+ sum = (Memd[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Memd[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ } else
+ sigma[i] = blank
+ }
+ }
+ }
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icsort.x b/pkg/images/immatch/src/imcombine/src/generic/icsort.x
new file mode 100644
index 00000000..3ec1d27e
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icsort.x
@@ -0,0 +1,1096 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define LOGPTR 32 # log2(maxpts) (4e9)
+
+
+# IC_SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially.
+
+procedure ic_sorts (a, b, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+short b[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+short pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR]
+define swap {temp=$1;$1=$2;$2=temp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix
+ b[i] = Mems[a[i]+l]
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) # bac
+ b[2] = pivot
+ else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) # acb
+ b[2] = temp3
+ else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ do i = 1, npix
+ b[i] = Mems[a[i]+l]
+
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix
+ Mems[a[i]+l] = b[i]
+ }
+end
+
+
+# IC_2SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially. A second integer set of
+# vectors is sorted.
+
+procedure ic_2sorts (a, b, c, d, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+short b[ARB] # work array
+pointer c[ARB] # pointer to associated integer vectors
+int d[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+short pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR], itemp
+define swap {temp=$1;$1=$2;$2=temp}
+define iswap {itemp=$1;$1=$2;$2=itemp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix {
+ b[i] = Mems[a[i]+l]
+ d[i] = Memi[c[i]+l]
+ }
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) { # bac
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ itemp = d[2]
+ d[2] = d[3]
+ d[3] = d[1]
+ d[1] = itemp
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ iswap (d[1], d[3])
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) { # acb
+ b[2] = temp3
+ iswap (d[2], d[3])
+ } else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ itemp = d[2]
+ d[2] = d[1]
+ d[1] = d[3]
+ d[3] = itemp
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k]); swap (d[j], d[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) { # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+ }
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix {
+ Mems[a[i]+l] = b[i]
+ Memi[c[i]+l] = d[i]
+ }
+ }
+end
+
+# IC_SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially.
+
+procedure ic_sorti (a, b, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+int b[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+int pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR]
+define swap {temp=$1;$1=$2;$2=temp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix
+ b[i] = Memi[a[i]+l]
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) # bac
+ b[2] = pivot
+ else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) # acb
+ b[2] = temp3
+ else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ do i = 1, npix
+ b[i] = Memi[a[i]+l]
+
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix
+ Memi[a[i]+l] = b[i]
+ }
+end
+
+
+# IC_2SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially. A second integer set of
+# vectors is sorted.
+
+procedure ic_2sorti (a, b, c, d, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+int b[ARB] # work array
+pointer c[ARB] # pointer to associated integer vectors
+int d[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+int pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR], itemp
+define swap {temp=$1;$1=$2;$2=temp}
+define iswap {itemp=$1;$1=$2;$2=itemp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix {
+ b[i] = Memi[a[i]+l]
+ d[i] = Memi[c[i]+l]
+ }
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) { # bac
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ itemp = d[2]
+ d[2] = d[3]
+ d[3] = d[1]
+ d[1] = itemp
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ iswap (d[1], d[3])
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) { # acb
+ b[2] = temp3
+ iswap (d[2], d[3])
+ } else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ itemp = d[2]
+ d[2] = d[1]
+ d[1] = d[3]
+ d[3] = itemp
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k]); swap (d[j], d[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) { # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+ }
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix {
+ Memi[a[i]+l] = b[i]
+ Memi[c[i]+l] = d[i]
+ }
+ }
+end
+
+# IC_SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially.
+
+procedure ic_sortr (a, b, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+real b[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+real pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR]
+define swap {temp=$1;$1=$2;$2=temp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix
+ b[i] = Memr[a[i]+l]
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) # bac
+ b[2] = pivot
+ else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) # acb
+ b[2] = temp3
+ else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ do i = 1, npix
+ b[i] = Memr[a[i]+l]
+
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix
+ Memr[a[i]+l] = b[i]
+ }
+end
+
+
+# IC_2SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially. A second integer set of
+# vectors is sorted.
+
+procedure ic_2sortr (a, b, c, d, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+real b[ARB] # work array
+pointer c[ARB] # pointer to associated integer vectors
+int d[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+real pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR], itemp
+define swap {temp=$1;$1=$2;$2=temp}
+define iswap {itemp=$1;$1=$2;$2=itemp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix {
+ b[i] = Memr[a[i]+l]
+ d[i] = Memi[c[i]+l]
+ }
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) { # bac
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ itemp = d[2]
+ d[2] = d[3]
+ d[3] = d[1]
+ d[1] = itemp
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ iswap (d[1], d[3])
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) { # acb
+ b[2] = temp3
+ iswap (d[2], d[3])
+ } else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ itemp = d[2]
+ d[2] = d[1]
+ d[1] = d[3]
+ d[3] = itemp
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k]); swap (d[j], d[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) { # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+ }
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix {
+ Memr[a[i]+l] = b[i]
+ Memi[c[i]+l] = d[i]
+ }
+ }
+end
+
+# IC_SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially.
+
+procedure ic_sortd (a, b, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+double b[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+double pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR]
+define swap {temp=$1;$1=$2;$2=temp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix
+ b[i] = Memd[a[i]+l]
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) # bac
+ b[2] = pivot
+ else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) # acb
+ b[2] = temp3
+ else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ do i = 1, npix
+ b[i] = Memd[a[i]+l]
+
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix
+ Memd[a[i]+l] = b[i]
+ }
+end
+
+
+# IC_2SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially. A second integer set of
+# vectors is sorted.
+
+procedure ic_2sortd (a, b, c, d, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+double b[ARB] # work array
+pointer c[ARB] # pointer to associated integer vectors
+int d[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+double pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR], itemp
+define swap {temp=$1;$1=$2;$2=temp}
+define iswap {itemp=$1;$1=$2;$2=itemp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix {
+ b[i] = Memd[a[i]+l]
+ d[i] = Memi[c[i]+l]
+ }
+
+ # Special cases
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) { # bac
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ itemp = d[2]
+ d[2] = d[3]
+ d[3] = d[1]
+ d[1] = itemp
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ iswap (d[1], d[3])
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) { # acb
+ b[2] = temp3
+ iswap (d[2], d[3])
+ } else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ itemp = d[2]
+ d[2] = d[1]
+ d[1] = d[3]
+ d[3] = itemp
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+
+ # General case
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k]); swap (d[j], d[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ for (i=i+1; b[i] < pivot; i=i+1)
+ ;
+ for (j=j-1; j > i; j=j-1)
+ if (b[j] <= pivot)
+ break
+ if (i < j) { # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+ }
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix {
+ Memd[a[i]+l] = b[i]
+ Memi[c[i]+l] = d[i]
+ }
+ }
+end
diff --git a/pkg/images/immatch/src/imcombine/src/generic/icstat.x b/pkg/images/immatch/src/imcombine/src/generic/icstat.x
new file mode 100644
index 00000000..3a0ed49c
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/icstat.x
@@ -0,0 +1,892 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include "../icombine.h"
+
+define NMAX 100000 # Maximum number of pixels to sample
+
+
+# IC_STAT -- Compute image statistics within specified section.
+# The image section is relative to a reference image which may be
+# different than the input image and may have an offset. Only a
+# subsample of pixels is used. Masked and thresholded pixels are
+# ignored. Only the desired statistics are computed to increase
+# efficiency.
+
+procedure ic_stats (im, imref, section, offsets, image, nimages,
+ domode, domedian, domean, mode, median, mean)
+
+pointer im # Data image
+pointer imref # Reference image for image section
+char section[ARB] # Image section
+int offsets[nimages,ARB] # Image section offset from data to reference
+int image # Image index (for mask I/O)
+int nimages # Number of images in offsets.
+bool domode, domedian, domean # Statistics to compute
+real mode, median, mean # Statistics
+
+int i, j, ndim, n, nv
+real a
+pointer sp, v1, v2, dv, va, vb
+pointer data, mask, dp, lp, mp, imgnls()
+
+real asums()
+short ic_modes()
+
+include "../icombine.com"
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (dv, IM_MAXDIM, TY_LONG)
+ call salloc (va, IM_MAXDIM, TY_LONG)
+ call salloc (vb, IM_MAXDIM, TY_LONG)
+
+ # Determine the image section parameters. This must be in terms of
+ # the data image pixel coordinates though the section may be specified
+ # in terms of the reference image coordinates. Limit the number of
+ # pixels in each dimension to a maximum.
+
+ ndim = IM_NDIM(im)
+ if (project)
+ ndim = ndim - 1
+ call amovki (1, Memi[v1], IM_MAXDIM)
+ call amovki (1, Memi[va], IM_MAXDIM)
+ call amovki (1, Memi[dv], IM_MAXDIM)
+ call amovi (IM_LEN(imref,1), Memi[vb], ndim)
+ call ic_section (section, Memi[va], Memi[vb], Memi[dv], ndim)
+ if (im != imref)
+ do i = 1, ndim {
+ Memi[va+i-1] = Memi[va+i-1] - offsets[image,i]
+ Memi[vb+i-1] = Memi[vb+i-1] - offsets[image,i]
+ }
+
+ do j = 1, 10 {
+ n = 1
+ do i = 0, ndim-1 {
+ Memi[v1+i] = max (1, min (Memi[va+i], Memi[vb+i]))
+ Memi[v2+i] = min (IM_LEN(im,i+1), max (Memi[va+i], Memi[vb+i]))
+ Memi[dv+i] = j
+ nv = max (1, (Memi[v2+i] - Memi[v1+i]) / Memi[dv+i] + 1)
+ Memi[v2+i] = Memi[v1+i] + (nv - 1) * Memi[dv+i]
+ n = n * nv
+ }
+ if (n < NMAX)
+ break
+ }
+
+ call amovl (Memi[v1], Memi[va], IM_MAXDIM)
+ Memi[va] = 1
+ if (project)
+ Memi[va+ndim] = image
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+
+ # Accumulate the pixel values within the section. Masked pixels and
+ # thresholded pixels are ignored.
+
+ call salloc (data, n, TY_SHORT)
+ dp = data
+ while (imgnls (im, lp, Memi[vb]) != EOF) {
+ call ic_mget1 (im, image, nimages, offsets[image,1], Memi[va], mask)
+ lp = lp + Memi[v1] - 1
+ if (dflag == D_ALL) {
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ a = Mems[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Mems[dp] = a
+ dp = dp + 1
+ }
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ Mems[dp] = Mems[lp]
+ dp = dp + 1
+ lp = lp + Memi[dv]
+ }
+ }
+ } else if (dflag == D_MIX) {
+ mp = mask + Memi[v1] - 1
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ a = Mems[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Mems[dp] = a
+ dp = dp + 1
+ }
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ Mems[dp] = Mems[lp]
+ dp = dp + 1
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ }
+ }
+ for (i=2; i<=ndim; i=i+1) {
+ Memi[va+i-1] = Memi[va+i-1] + Memi[dv+i-1]
+ if (Memi[va+i-1] <= Memi[v2+i-1])
+ break
+ Memi[va+i-1] = Memi[v1+i-1]
+ }
+ if (i > ndim)
+ break
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+ }
+
+ # Close mask until it is needed again.
+ call ic_mclose1 (image, nimages)
+
+ n = dp - data
+ if (n < 1) {
+ call sfree (sp)
+ call error (1, "Image section contains no pixels")
+ }
+
+ # Compute only statistics needed.
+ if (domode || domedian) {
+ call asrts (Mems[data], Mems[data], n)
+ mode = ic_modes (Mems[data], n)
+ median = Mems[data+n/2-1]
+ }
+ if (domean)
+ mean = asums (Mems[data], n) / n
+
+ call sfree (sp)
+end
+
+
+define NMIN 10 # Minimum number of pixels for mode calculation
+define ZRANGE 0.7 # Fraction of pixels about median to use
+define ZSTEP 0.01 # Step size for search for mode
+define ZBIN 0.1 # Bin size for mode.
+
+# IC_MODE -- Compute mode of an array. The mode is found by binning
+# with a bin size based on the data range over a fraction of the
+# pixels about the median and a bin step which may be smaller than the
+# bin size. If there are too few points the median is returned.
+# The input array must be sorted.
+
+short procedure ic_modes (a, n)
+
+short a[n] # Data array
+int n # Number of points
+
+int i, j, k, nmax
+real z1, z2, zstep, zbin
+short mode
+bool fp_equalr()
+
+begin
+ if (n < NMIN)
+ return (a[n/2])
+
+ # Compute the mode. The array must be sorted. Consider a
+ # range of values about the median point. Use a bin size which
+ # is ZBIN of the range. Step the bin limits in ZSTEP fraction of
+ # the bin size.
+
+ i = 1 + n * (1. - ZRANGE) / 2.
+ j = 1 + n * (1. + ZRANGE) / 2.
+ z1 = a[i]
+ z2 = a[j]
+ if (fp_equalr (z1, z2)) {
+ mode = z1
+ return (mode)
+ }
+
+ zstep = ZSTEP * (z2 - z1)
+ zbin = ZBIN * (z2 - z1)
+ zstep = max (1., zstep)
+ zbin = max (1., zbin)
+
+ z1 = z1 - zstep
+ k = i
+ nmax = 0
+ repeat {
+ z1 = z1 + zstep
+ z2 = z1 + zbin
+ for (; i < j && a[i] < z1; i=i+1)
+ ;
+ for (; k < j && a[k] < z2; k=k+1)
+ ;
+ if (k - i > nmax) {
+ nmax = k - i
+ mode = a[(i+k)/2]
+ }
+ } until (k >= j)
+
+ return (mode)
+end
+
+# IC_STAT -- Compute image statistics within specified section.
+# The image section is relative to a reference image which may be
+# different than the input image and may have an offset. Only a
+# subsample of pixels is used. Masked and thresholded pixels are
+# ignored. Only the desired statistics are computed to increase
+# efficiency.
+
+procedure ic_stati (im, imref, section, offsets, image, nimages,
+ domode, domedian, domean, mode, median, mean)
+
+pointer im # Data image
+pointer imref # Reference image for image section
+char section[ARB] # Image section
+int offsets[nimages,ARB] # Image section offset from data to reference
+int image # Image index (for mask I/O)
+int nimages # Number of images in offsets.
+bool domode, domedian, domean # Statistics to compute
+real mode, median, mean # Statistics
+
+int i, j, ndim, n, nv
+real a
+pointer sp, v1, v2, dv, va, vb
+pointer data, mask, dp, lp, mp, imgnli()
+
+real asumi()
+int ic_modei()
+
+include "../icombine.com"
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (dv, IM_MAXDIM, TY_LONG)
+ call salloc (va, IM_MAXDIM, TY_LONG)
+ call salloc (vb, IM_MAXDIM, TY_LONG)
+
+ # Determine the image section parameters. This must be in terms of
+ # the data image pixel coordinates though the section may be specified
+ # in terms of the reference image coordinates. Limit the number of
+ # pixels in each dimension to a maximum.
+
+ ndim = IM_NDIM(im)
+ if (project)
+ ndim = ndim - 1
+ call amovki (1, Memi[v1], IM_MAXDIM)
+ call amovki (1, Memi[va], IM_MAXDIM)
+ call amovki (1, Memi[dv], IM_MAXDIM)
+ call amovi (IM_LEN(imref,1), Memi[vb], ndim)
+ call ic_section (section, Memi[va], Memi[vb], Memi[dv], ndim)
+ if (im != imref)
+ do i = 1, ndim {
+ Memi[va+i-1] = Memi[va+i-1] - offsets[image,i]
+ Memi[vb+i-1] = Memi[vb+i-1] - offsets[image,i]
+ }
+
+ do j = 1, 10 {
+ n = 1
+ do i = 0, ndim-1 {
+ Memi[v1+i] = max (1, min (Memi[va+i], Memi[vb+i]))
+ Memi[v2+i] = min (IM_LEN(im,i+1), max (Memi[va+i], Memi[vb+i]))
+ Memi[dv+i] = j
+ nv = max (1, (Memi[v2+i] - Memi[v1+i]) / Memi[dv+i] + 1)
+ Memi[v2+i] = Memi[v1+i] + (nv - 1) * Memi[dv+i]
+ n = n * nv
+ }
+ if (n < NMAX)
+ break
+ }
+
+ call amovl (Memi[v1], Memi[va], IM_MAXDIM)
+ Memi[va] = 1
+ if (project)
+ Memi[va+ndim] = image
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+
+ # Accumulate the pixel values within the section. Masked pixels and
+ # thresholded pixels are ignored.
+
+ call salloc (data, n, TY_INT)
+ dp = data
+ while (imgnli (im, lp, Memi[vb]) != EOF) {
+ call ic_mget1 (im, image, nimages, offsets[image,1], Memi[va], mask)
+ lp = lp + Memi[v1] - 1
+ if (dflag == D_ALL) {
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ a = Memi[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Memi[dp] = a
+ dp = dp + 1
+ }
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ Memi[dp] = Memi[lp]
+ dp = dp + 1
+ lp = lp + Memi[dv]
+ }
+ }
+ } else if (dflag == D_MIX) {
+ mp = mask + Memi[v1] - 1
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ a = Memi[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Memi[dp] = a
+ dp = dp + 1
+ }
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ Memi[dp] = Memi[lp]
+ dp = dp + 1
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ }
+ }
+ for (i=2; i<=ndim; i=i+1) {
+ Memi[va+i-1] = Memi[va+i-1] + Memi[dv+i-1]
+ if (Memi[va+i-1] <= Memi[v2+i-1])
+ break
+ Memi[va+i-1] = Memi[v1+i-1]
+ }
+ if (i > ndim)
+ break
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+ }
+
+ # Close mask until it is needed again.
+ call ic_mclose1 (image, nimages)
+
+ n = dp - data
+ if (n < 1) {
+ call sfree (sp)
+ call error (1, "Image section contains no pixels")
+ }
+
+ # Compute only statistics needed.
+ if (domode || domedian) {
+ call asrti (Memi[data], Memi[data], n)
+ mode = ic_modei (Memi[data], n)
+ median = Memi[data+n/2-1]
+ }
+ if (domean)
+ mean = asumi (Memi[data], n) / n
+
+ call sfree (sp)
+end
+
+
+define NMIN 10 # Minimum number of pixels for mode calculation
+define ZRANGE 0.7 # Fraction of pixels about median to use
+define ZSTEP 0.01 # Step size for search for mode
+define ZBIN 0.1 # Bin size for mode.
+
+# IC_MODE -- Compute mode of an array. The mode is found by binning
+# with a bin size based on the data range over a fraction of the
+# pixels about the median and a bin step which may be smaller than the
+# bin size. If there are too few points the median is returned.
+# The input array must be sorted.
+
+int procedure ic_modei (a, n)
+
+int a[n] # Data array
+int n # Number of points
+
+int i, j, k, nmax
+real z1, z2, zstep, zbin
+int mode
+bool fp_equalr()
+
+begin
+ if (n < NMIN)
+ return (a[n/2])
+
+ # Compute the mode. The array must be sorted. Consider a
+ # range of values about the median point. Use a bin size which
+ # is ZBIN of the range. Step the bin limits in ZSTEP fraction of
+ # the bin size.
+
+ i = 1 + n * (1. - ZRANGE) / 2.
+ j = 1 + n * (1. + ZRANGE) / 2.
+ z1 = a[i]
+ z2 = a[j]
+ if (fp_equalr (z1, z2)) {
+ mode = z1
+ return (mode)
+ }
+
+ zstep = ZSTEP * (z2 - z1)
+ zbin = ZBIN * (z2 - z1)
+ zstep = max (1., zstep)
+ zbin = max (1., zbin)
+
+ z1 = z1 - zstep
+ k = i
+ nmax = 0
+ repeat {
+ z1 = z1 + zstep
+ z2 = z1 + zbin
+ for (; i < j && a[i] < z1; i=i+1)
+ ;
+ for (; k < j && a[k] < z2; k=k+1)
+ ;
+ if (k - i > nmax) {
+ nmax = k - i
+ mode = a[(i+k)/2]
+ }
+ } until (k >= j)
+
+ return (mode)
+end
+
+# IC_STAT -- Compute image statistics within specified section.
+# The image section is relative to a reference image which may be
+# different than the input image and may have an offset. Only a
+# subsample of pixels is used. Masked and thresholded pixels are
+# ignored. Only the desired statistics are computed to increase
+# efficiency.
+
+procedure ic_statr (im, imref, section, offsets, image, nimages,
+ domode, domedian, domean, mode, median, mean)
+
+pointer im # Data image
+pointer imref # Reference image for image section
+char section[ARB] # Image section
+int offsets[nimages,ARB] # Image section offset from data to reference
+int image # Image index (for mask I/O)
+int nimages # Number of images in offsets.
+bool domode, domedian, domean # Statistics to compute
+real mode, median, mean # Statistics
+
+int i, j, ndim, n, nv
+real a
+pointer sp, v1, v2, dv, va, vb
+pointer data, mask, dp, lp, mp, imgnlr()
+
+real asumr()
+real ic_moder()
+
+include "../icombine.com"
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (dv, IM_MAXDIM, TY_LONG)
+ call salloc (va, IM_MAXDIM, TY_LONG)
+ call salloc (vb, IM_MAXDIM, TY_LONG)
+
+ # Determine the image section parameters. This must be in terms of
+ # the data image pixel coordinates though the section may be specified
+ # in terms of the reference image coordinates. Limit the number of
+ # pixels in each dimension to a maximum.
+
+ ndim = IM_NDIM(im)
+ if (project)
+ ndim = ndim - 1
+ call amovki (1, Memi[v1], IM_MAXDIM)
+ call amovki (1, Memi[va], IM_MAXDIM)
+ call amovki (1, Memi[dv], IM_MAXDIM)
+ call amovi (IM_LEN(imref,1), Memi[vb], ndim)
+ call ic_section (section, Memi[va], Memi[vb], Memi[dv], ndim)
+ if (im != imref)
+ do i = 1, ndim {
+ Memi[va+i-1] = Memi[va+i-1] - offsets[image,i]
+ Memi[vb+i-1] = Memi[vb+i-1] - offsets[image,i]
+ }
+
+ do j = 1, 10 {
+ n = 1
+ do i = 0, ndim-1 {
+ Memi[v1+i] = max (1, min (Memi[va+i], Memi[vb+i]))
+ Memi[v2+i] = min (IM_LEN(im,i+1), max (Memi[va+i], Memi[vb+i]))
+ Memi[dv+i] = j
+ nv = max (1, (Memi[v2+i] - Memi[v1+i]) / Memi[dv+i] + 1)
+ Memi[v2+i] = Memi[v1+i] + (nv - 1) * Memi[dv+i]
+ n = n * nv
+ }
+ if (n < NMAX)
+ break
+ }
+
+ call amovl (Memi[v1], Memi[va], IM_MAXDIM)
+ Memi[va] = 1
+ if (project)
+ Memi[va+ndim] = image
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+
+ # Accumulate the pixel values within the section. Masked pixels and
+ # thresholded pixels are ignored.
+
+ call salloc (data, n, TY_REAL)
+ dp = data
+ while (imgnlr (im, lp, Memi[vb]) != EOF) {
+ call ic_mget1 (im, image, nimages, offsets[image,1], Memi[va], mask)
+ lp = lp + Memi[v1] - 1
+ if (dflag == D_ALL) {
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ a = Memr[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Memr[dp] = a
+ dp = dp + 1
+ }
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ Memr[dp] = Memr[lp]
+ dp = dp + 1
+ lp = lp + Memi[dv]
+ }
+ }
+ } else if (dflag == D_MIX) {
+ mp = mask + Memi[v1] - 1
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ a = Memr[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Memr[dp] = a
+ dp = dp + 1
+ }
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ Memr[dp] = Memr[lp]
+ dp = dp + 1
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ }
+ }
+ for (i=2; i<=ndim; i=i+1) {
+ Memi[va+i-1] = Memi[va+i-1] + Memi[dv+i-1]
+ if (Memi[va+i-1] <= Memi[v2+i-1])
+ break
+ Memi[va+i-1] = Memi[v1+i-1]
+ }
+ if (i > ndim)
+ break
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+ }
+
+ # Close mask until it is needed again.
+ call ic_mclose1 (image, nimages)
+
+ n = dp - data
+ if (n < 1) {
+ call sfree (sp)
+ call error (1, "Image section contains no pixels")
+ }
+
+ # Compute only statistics needed.
+ if (domode || domedian) {
+ call asrtr (Memr[data], Memr[data], n)
+ mode = ic_moder (Memr[data], n)
+ median = Memr[data+n/2-1]
+ }
+ if (domean)
+ mean = asumr (Memr[data], n) / n
+
+ call sfree (sp)
+end
+
+
+define NMIN 10 # Minimum number of pixels for mode calculation
+define ZRANGE 0.7 # Fraction of pixels about median to use
+define ZSTEP 0.01 # Step size for search for mode
+define ZBIN 0.1 # Bin size for mode.
+
+# IC_MODE -- Compute mode of an array. The mode is found by binning
+# with a bin size based on the data range over a fraction of the
+# pixels about the median and a bin step which may be smaller than the
+# bin size. If there are too few points the median is returned.
+# The input array must be sorted.
+
+real procedure ic_moder (a, n)
+
+real a[n] # Data array
+int n # Number of points
+
+int i, j, k, nmax
+real z1, z2, zstep, zbin
+real mode
+bool fp_equalr()
+
+begin
+ if (n < NMIN)
+ return (a[n/2])
+
+ # Compute the mode. The array must be sorted. Consider a
+ # range of values about the median point. Use a bin size which
+ # is ZBIN of the range. Step the bin limits in ZSTEP fraction of
+ # the bin size.
+
+ i = 1 + n * (1. - ZRANGE) / 2.
+ j = 1 + n * (1. + ZRANGE) / 2.
+ z1 = a[i]
+ z2 = a[j]
+ if (fp_equalr (z1, z2)) {
+ mode = z1
+ return (mode)
+ }
+
+ zstep = ZSTEP * (z2 - z1)
+ zbin = ZBIN * (z2 - z1)
+
+ z1 = z1 - zstep
+ k = i
+ nmax = 0
+ repeat {
+ z1 = z1 + zstep
+ z2 = z1 + zbin
+ for (; i < j && a[i] < z1; i=i+1)
+ ;
+ for (; k < j && a[k] < z2; k=k+1)
+ ;
+ if (k - i > nmax) {
+ nmax = k - i
+ mode = a[(i+k)/2]
+ }
+ } until (k >= j)
+
+ return (mode)
+end
+
+# IC_STAT -- Compute image statistics within specified section.
+# The image section is relative to a reference image which may be
+# different than the input image and may have an offset. Only a
+# subsample of pixels is used. Masked and thresholded pixels are
+# ignored. Only the desired statistics are computed to increase
+# efficiency.
+
+procedure ic_statd (im, imref, section, offsets, image, nimages,
+ domode, domedian, domean, mode, median, mean)
+
+pointer im # Data image
+pointer imref # Reference image for image section
+char section[ARB] # Image section
+int offsets[nimages,ARB] # Image section offset from data to reference
+int image # Image index (for mask I/O)
+int nimages # Number of images in offsets.
+bool domode, domedian, domean # Statistics to compute
+real mode, median, mean # Statistics
+
+int i, j, ndim, n, nv
+real a
+pointer sp, v1, v2, dv, va, vb
+pointer data, mask, dp, lp, mp, imgnld()
+
+double asumd()
+double ic_moded()
+
+include "../icombine.com"
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (dv, IM_MAXDIM, TY_LONG)
+ call salloc (va, IM_MAXDIM, TY_LONG)
+ call salloc (vb, IM_MAXDIM, TY_LONG)
+
+ # Determine the image section parameters. This must be in terms of
+ # the data image pixel coordinates though the section may be specified
+ # in terms of the reference image coordinates. Limit the number of
+ # pixels in each dimension to a maximum.
+
+ ndim = IM_NDIM(im)
+ if (project)
+ ndim = ndim - 1
+ call amovki (1, Memi[v1], IM_MAXDIM)
+ call amovki (1, Memi[va], IM_MAXDIM)
+ call amovki (1, Memi[dv], IM_MAXDIM)
+ call amovi (IM_LEN(imref,1), Memi[vb], ndim)
+ call ic_section (section, Memi[va], Memi[vb], Memi[dv], ndim)
+ if (im != imref)
+ do i = 1, ndim {
+ Memi[va+i-1] = Memi[va+i-1] - offsets[image,i]
+ Memi[vb+i-1] = Memi[vb+i-1] - offsets[image,i]
+ }
+
+ do j = 1, 10 {
+ n = 1
+ do i = 0, ndim-1 {
+ Memi[v1+i] = max (1, min (Memi[va+i], Memi[vb+i]))
+ Memi[v2+i] = min (IM_LEN(im,i+1), max (Memi[va+i], Memi[vb+i]))
+ Memi[dv+i] = j
+ nv = max (1, (Memi[v2+i] - Memi[v1+i]) / Memi[dv+i] + 1)
+ Memi[v2+i] = Memi[v1+i] + (nv - 1) * Memi[dv+i]
+ n = n * nv
+ }
+ if (n < NMAX)
+ break
+ }
+
+ call amovl (Memi[v1], Memi[va], IM_MAXDIM)
+ Memi[va] = 1
+ if (project)
+ Memi[va+ndim] = image
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+
+ # Accumulate the pixel values within the section. Masked pixels and
+ # thresholded pixels are ignored.
+
+ call salloc (data, n, TY_DOUBLE)
+ dp = data
+ while (imgnld (im, lp, Memi[vb]) != EOF) {
+ call ic_mget1 (im, image, nimages, offsets[image,1], Memi[va], mask)
+ lp = lp + Memi[v1] - 1
+ if (dflag == D_ALL) {
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ a = Memd[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Memd[dp] = a
+ dp = dp + 1
+ }
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ Memd[dp] = Memd[lp]
+ dp = dp + 1
+ lp = lp + Memi[dv]
+ }
+ }
+ } else if (dflag == D_MIX) {
+ mp = mask + Memi[v1] - 1
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ a = Memd[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Memd[dp] = a
+ dp = dp + 1
+ }
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ Memd[dp] = Memd[lp]
+ dp = dp + 1
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ }
+ }
+ for (i=2; i<=ndim; i=i+1) {
+ Memi[va+i-1] = Memi[va+i-1] + Memi[dv+i-1]
+ if (Memi[va+i-1] <= Memi[v2+i-1])
+ break
+ Memi[va+i-1] = Memi[v1+i-1]
+ }
+ if (i > ndim)
+ break
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+ }
+
+ # Close mask until it is needed again.
+ call ic_mclose1 (image, nimages)
+
+ n = dp - data
+ if (n < 1) {
+ call sfree (sp)
+ call error (1, "Image section contains no pixels")
+ }
+
+ # Compute only statistics needed.
+ if (domode || domedian) {
+ call asrtd (Memd[data], Memd[data], n)
+ mode = ic_moded (Memd[data], n)
+ median = Memd[data+n/2-1]
+ }
+ if (domean)
+ mean = asumd (Memd[data], n) / n
+
+ call sfree (sp)
+end
+
+
+define NMIN 10 # Minimum number of pixels for mode calculation
+define ZRANGE 0.7 # Fraction of pixels about median to use
+define ZSTEP 0.01 # Step size for search for mode
+define ZBIN 0.1 # Bin size for mode.
+
+# IC_MODE -- Compute mode of an array. The mode is found by binning
+# with a bin size based on the data range over a fraction of the
+# pixels about the median and a bin step which may be smaller than the
+# bin size. If there are too few points the median is returned.
+# The input array must be sorted.
+
+double procedure ic_moded (a, n)
+
+double a[n] # Data array
+int n # Number of points
+
+int i, j, k, nmax
+real z1, z2, zstep, zbin
+double mode
+bool fp_equalr()
+
+begin
+ if (n < NMIN)
+ return (a[n/2])
+
+ # Compute the mode. The array must be sorted. Consider a
+ # range of values about the median point. Use a bin size which
+ # is ZBIN of the range. Step the bin limits in ZSTEP fraction of
+ # the bin size.
+
+ i = 1 + n * (1. - ZRANGE) / 2.
+ j = 1 + n * (1. + ZRANGE) / 2.
+ z1 = a[i]
+ z2 = a[j]
+ if (fp_equalr (z1, z2)) {
+ mode = z1
+ return (mode)
+ }
+
+ zstep = ZSTEP * (z2 - z1)
+ zbin = ZBIN * (z2 - z1)
+
+ z1 = z1 - zstep
+ k = i
+ nmax = 0
+ repeat {
+ z1 = z1 + zstep
+ z2 = z1 + zbin
+ for (; i < j && a[i] < z1; i=i+1)
+ ;
+ for (; k < j && a[k] < z2; k=k+1)
+ ;
+ if (k - i > nmax) {
+ nmax = k - i
+ mode = a[(i+k)/2]
+ }
+ } until (k >= j)
+
+ return (mode)
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/generic/mkpkg b/pkg/images/immatch/src/imcombine/src/generic/mkpkg
new file mode 100644
index 00000000..af2fd0a8
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/mkpkg
@@ -0,0 +1,27 @@
+# Make IMCOMBINE.
+
+$checkout libimc.a lib$
+$update libimc.a
+$checkin libimc.a lib$
+$exit
+
+libimc.a:
+ icaclip.x ../icombine.com ../icombine.h
+ icaverage.x ../icombine.com ../icombine.h <imhdr.h>
+ iccclip.x ../icombine.com ../icombine.h
+ icgdata.x ../icombine.com ../icombine.h <imhdr.h> <mach.h>
+ icgrow.x ../icombine.com ../icombine.h <imhdr.h> <pmset.h>
+ icmedian.x ../icombine.com ../icombine.h
+ icmm.x ../icombine.com ../icombine.h
+ icnmodel.x ../icombine.com ../icombine.h <imhdr.h>
+ icomb.x ../icombine.com ../icombine.h <error.h> <imhdr.h>\
+ <imset.h> <mach.h> <pmset.h> <syserr.h>
+ icpclip.x ../icombine.com ../icombine.h
+ icquad.x ../icombine.com ../icombine.h <imhdr.h>
+ icsclip.x ../icombine.com ../icombine.h
+ icsigma.x ../icombine.com ../icombine.h <imhdr.h>
+ icsort.x
+ icstat.x ../icombine.com ../icombine.h <imhdr.h>
+
+ xtimmap.x xtimmap.com <config.h> <error.h> <imhdr.h> <imset.h>
+ ;
diff --git a/pkg/images/immatch/src/imcombine/src/generic/xtimmap.com b/pkg/images/immatch/src/imcombine/src/generic/xtimmap.com
new file mode 100644
index 00000000..57fcb8a0
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/xtimmap.com
@@ -0,0 +1,9 @@
+int option
+int nopen
+int nopenpix
+int nalloc
+int last_flag
+int min_open
+int max_openim
+pointer ims
+common /xtimmapcom/ option, ims, nopen, nopenpix, nalloc, last_flag, min_open, max_openim
diff --git a/pkg/images/immatch/src/imcombine/src/generic/xtimmap.x b/pkg/images/immatch/src/imcombine/src/generic/xtimmap.x
new file mode 100644
index 00000000..fcc53124
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/generic/xtimmap.x
@@ -0,0 +1,1207 @@
+include <syserr.h>
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+include <config.h>
+
+# The following is for compiling under V2.11.
+define IM_BUFFRAC IM_BUFSIZE
+include <imset.h>
+
+define VERBOSE false
+
+# These routines maintain an arbitrary number of indexed "open" images which
+# must be READ_ONLY. The calling program may use the returned pointer for
+# header accesses but must call xt_opix before I/O. Subsequent calls to
+# xt_opix may invalidate the pointer. The xt_imunmap call will free memory.
+
+define MAX_OPENIM (LAST_FD-16) # Maximum images kept open
+define MAX_OPENPIX 45 # Maximum pixel files kept open
+
+define XT_SZIMNAME 299 # Size of IMNAME string
+define XT_LEN 179 # Structure length
+define XT_IMNAME Memc[P2C($1)] # Image name
+define XT_ARG Memi[$1+150] # IMMAP header argument
+define XT_IM Memi[$1+151] # IMIO pointer
+define XT_HDR Memi[$1+152] # Copy of IMIO pointer
+define XT_CLOSEFD Memi[$1+153] # Close FD?
+define XT_FLAG Memi[$1+154] # Flag
+define XT_BUFSIZE Memi[$1+155] # Buffer size
+define XT_BUF Memi[$1+156] # Data buffer
+define XT_BTYPE Memi[$1+157] # Data buffer type
+define XT_VS Memi[$1+157+$2] # Start vector (10)
+define XT_VE Memi[$1+167+$2] # End vector (10)
+
+# Options
+define XT_MAPUNMAP 1 # Map and unmap images.
+
+# XT_IMMAP -- Map an image and save it as an indexed open image.
+# The returned pointer may be used for header access but not I/O.
+# The indexed image is closed by xt_imunmap.
+
+pointer procedure xt_immap (imname, acmode, hdr_arg, index, retry)
+
+char imname[ARB] #I Image name
+int acmode #I Access mode
+int hdr_arg #I Header argument
+int index #I Save index
+int retry #I Retry counter
+pointer im #O Image pointer (returned)
+
+int i, envgeti()
+pointer xt, xt_opix()
+errchk xt_opix
+
+int first_time
+data first_time /YES/
+
+include "xtimmap.com"
+
+begin
+ if (acmode != READ_ONLY)
+ call error (1, "XT_IMMAP: Only READ_ONLY allowed")
+
+ # Set maximum number of open images based on retry.
+ if (retry > 0)
+ max_openim = min (1024, MAX_OPENIM) / retry
+ else
+ max_openim = MAX_OPENIM
+
+ # Initialize once per process.
+ if (first_time == YES) {
+ iferr (option = envgeti ("imcombine_option"))
+ option = 1
+ min_open = 1
+ nopen = 0
+ nopenpix = 0
+ nalloc = max_openim
+ call calloc (ims, nalloc, TY_POINTER)
+ first_time = NO
+ }
+
+ # Free image if needed.
+ call xt_imunmap (NULL, index)
+
+ # Allocate structure.
+ if (index > nalloc) {
+ i = nalloc
+ nalloc = index + max_openim
+ call realloc (ims, nalloc, TY_STRUCT)
+ call amovki (NULL, Memi[ims+i], nalloc-i)
+ }
+ call calloc (xt, XT_LEN, TY_STRUCT)
+ Memi[ims+index-1] = xt
+
+ # Initialize.
+ call strcpy (imname, XT_IMNAME(xt), XT_SZIMNAME)
+ XT_ARG(xt) = hdr_arg
+ XT_IM(xt) = NULL
+ XT_HDR(xt) = NULL
+
+ # Open image.
+ last_flag = 0
+ im = xt_opix (NULL, index, 0)
+
+ # Make copy of IMIO pointer for header keyword access.
+ call malloc (XT_HDR(xt), LEN_IMDES+IM_HDRLEN(im)+1, TY_STRUCT)
+ call amovi (Memi[im], Memi[XT_HDR(xt)], LEN_IMDES)
+ call amovi (IM_MAGIC(im), IM_MAGIC(XT_HDR(xt)), IM_HDRLEN(im)+1)
+
+ return (XT_HDR(xt))
+end
+
+
+# XT_OPIX -- Open the image for I/O.
+# If the image has not been mapped return the default pointer.
+
+pointer procedure xt_opix (imdef, index, flag)
+
+int index #I index
+pointer imdef #I Default pointer
+int flag #I Flag
+
+int i, open(), imstati()
+pointer im, xt, xt1, immap()
+errchk open, immap, imunmap
+
+include "xtimmap.com"
+
+begin
+ # Get index pointer.
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ # Use default pointer if index has not been mapped.
+ if (xt == NULL)
+ return (imdef)
+
+ # Close images not accessed during previous line.
+ # In normal usage this should only occur once per line over all
+ # indexed images.
+ if (flag != last_flag) {
+ do i = 1, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL || XT_FLAG(xt1) == last_flag)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: xt_opix imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ call mfree (XT_BUF(xt1), XT_BTYPE(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ }
+
+ # Optimize the file I/O.
+ do i = nalloc, 1, -1 {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ if (nopenpix < MAX_OPENPIX) {
+ if (XT_CLOSEFD(xt1) == NO)
+ next
+ XT_CLOSEFD(xt1) = NO
+ call imseti (im, IM_CLOSEFD, NO)
+ nopenpix = nopenpix + 1
+ }
+ }
+ last_flag = flag
+ }
+
+ # Return pointer for already opened images.
+ im = XT_IM(xt)
+ if (im != NULL) {
+ XT_FLAG(xt) = flag
+ return (im)
+ }
+
+ # Handle more images than the maximum that can be open at one time.
+ if (nopen >= max_openim) {
+ if (option == XT_MAPUNMAP || flag == 0) {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ min_open = i + 1
+ break
+ }
+ if (index <= min_open)
+ min_open = index
+ else {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ break
+ }
+ }
+ } else {
+ # Check here because we can't catch error in immap.
+ i = open ("dev$null", READ_ONLY, BINARY_FILE)
+ call close (i)
+ if (i == LAST_FD - 1)
+ call error (SYS_FTOOMANYFILES, "Too many open files")
+ }
+ }
+
+ # Open image.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_opix immap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ im = immap (XT_IMNAME(xt), READ_ONLY, XT_ARG(xt))
+ XT_IM(xt) = im
+ if (!IS_INDEFI(XT_BUFSIZE(xt)))
+ call imseti (im, IM_BUFSIZE, XT_BUFSIZE(xt))
+ else
+ XT_BUFSIZE(xt) = imstati (im, IM_BUFSIZE)
+ nopen = nopen + 1
+ XT_CLOSEFD(xt) = YES
+ if (nopenpix < MAX_OPENPIX) {
+ XT_CLOSEFD(xt) = NO
+ nopenpix = nopenpix + 1
+ }
+ if (XT_CLOSEFD(xt) == YES)
+ call imseti (im, IM_CLOSEFD, YES)
+ XT_FLAG(xt) = flag
+
+ return (im)
+end
+
+
+# XT_CPIX -- Close image.
+
+procedure xt_cpix (index)
+
+int index #I index
+
+pointer xt
+errchk imunmap
+
+include "xtimmap.com"
+
+begin
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ if (xt == NULL)
+ return
+
+ if (XT_IM(xt) != NULL) {
+ if (VERBOSE) {
+ call eprintf ("%d: xt_cpix imunmap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ call imunmap (XT_IM(xt))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt) == NO)
+ nopenpix = nopenpix - 1
+ }
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+end
+
+
+# XT_IMSETI -- Set IMIO value.
+
+procedure xt_imseti (index, param, value)
+
+int index #I index
+int param #I IMSET parameter
+int value #I Value
+
+pointer xt
+bool streq()
+
+include "xtimmap.com"
+
+begin
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ if (xt == NULL) {
+ if (streq (param, "option"))
+ option = value
+ } else {
+ if (streq (param, "bufsize")) {
+ XT_BUFSIZE(xt) = value
+ if (XT_IM(xt) != NULL) {
+ call imseti (XT_IM(xt), IM_BUFFRAC, 0)
+ call imseti (XT_IM(xt), IM_BUFSIZE, value)
+ }
+ }
+ }
+end
+
+
+# XT_IMUNMAP -- Unmap indexed open image.
+# The header pointer is set to NULL to indicate the image has been closed.
+
+procedure xt_imunmap (im, index)
+
+int im #U IMIO header pointer
+int index #I index
+
+pointer xt
+errchk imunmap
+
+include "xtimmap.com"
+
+begin
+ # Check for an indexed image. If it is not unmap the pointer
+ # as a regular IMIO pointer.
+
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+ if (xt == NULL) {
+ if (im != NULL)
+ call imunmap (im)
+ return
+ }
+
+ # Close indexed image.
+ if (XT_IM(xt) != NULL) {
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imunmap imunmap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ iferr (call imunmap (XT_IM(xt))) {
+ XT_IM(xt) = NULL
+ call erract (EA_WARN)
+ }
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt) == NO)
+ nopenpix = nopenpix - 1
+ if (index == min_open)
+ min_open = 1
+ }
+
+ # Free any buffered memory.
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+
+ # Free header pointer. Note that if the supplied pointer is not
+ # header pointer then it is not set to NULL.
+ if (XT_HDR(xt) == im)
+ im = NULL
+ call mfree (XT_HDR(xt), TY_STRUCT)
+
+ # Free save structure.
+ call mfree (Memi[ims+index-1], TY_STRUCT)
+ Memi[ims+index-1] = NULL
+end
+
+
+# XT_MINHDR -- Minimize header assuming keywords will not be accessed.
+
+procedure xt_minhdr (index)
+
+int index #I index
+
+pointer xt
+errchk realloc
+
+include "xtimmap.com"
+
+begin
+ # Check for an indexed image. If it is not unmap the pointer
+ # as a regular IMIO pointer.
+
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+ if (xt == NULL)
+ return
+
+ # Minimize header pointer.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_minhdr %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ call realloc (XT_HDR(xt), IMU+1, TY_STRUCT)
+ if (XT_IM(xt) != NULL)
+ call realloc (XT_IM(xt), IMU+1, TY_STRUCT)
+end
+
+
+# XT_REINDEX -- Reindex open images.
+# This is used when some images are closed by xt_imunmap. It is up to
+# the calling program to reindex the header pointers and to subsequently
+# use the new index values.
+
+procedure xt_reindex ()
+
+int old, new
+
+include "xtimmap.com"
+
+begin
+ new = 0
+ do old = 0, nalloc-1 {
+ if (Memi[ims+old] == NULL)
+ next
+ Memi[ims+new] = Memi[ims+old]
+ new = new + 1
+ }
+ do old = new, nalloc-1
+ Memi[ims+old] = NULL
+end
+
+
+
+# XT_IMGNL -- Return the next line for the indexed image.
+# Possibly unmap another image if too many files are open.
+# Buffer data when an image is unmmaped to minimize the mapping of images.
+# If the requested index has not been mapped use the default pointer.
+
+int procedure xt_imgnls (imdef, index, buf, v, flag)
+
+pointer imdef #I Default pointer
+int index #I index
+pointer buf #O Data buffer
+long v[ARB] #I Line vector
+int flag #I Flag (=output line)
+
+int i, j, nc, nl, open(), imgnls(), sizeof(), imloop()
+pointer im, xt, xt1, ptr, immap(), imggss()
+errchk open, immap, imgnls, imggss, imunmap
+
+long unit_v[IM_MAXDIM]
+data unit_v /IM_MAXDIM * 1/
+
+include "xtimmap.com"
+
+begin
+ # Get index pointer.
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ # Use default pointer if index has not been mapped.
+ if (xt == NULL)
+ return (imgnls (imdef, buf, v))
+
+ # Close images not accessed during previous line.
+ # In normal usage this should only occur once per line over all
+ # indexed images.
+ if (flag != last_flag) {
+ do i = 1, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL || XT_FLAG(xt1) == last_flag)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ call mfree (XT_BUF(xt1), XT_BTYPE(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ }
+
+ # Optimize the file I/O.
+ do i = nalloc, 1, -1 {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ if (nopenpix < MAX_OPENPIX) {
+ if (XT_CLOSEFD(xt1) == NO)
+ next
+ XT_CLOSEFD(xt1) = NO
+ call imseti (im, IM_CLOSEFD, NO)
+ nopenpix = nopenpix + 1
+ }
+ }
+ last_flag = flag
+ }
+
+ # Use IMIO for already opened images.
+ im = XT_IM(xt)
+ if (im != NULL) {
+ XT_FLAG(xt) = flag
+ return (imgnls (im, buf, v))
+ }
+
+ # If the image is not currently mapped use the stored header.
+ im = XT_HDR(xt)
+
+ # Check for EOF.
+ i = IM_NDIM(im)
+ if (v[i] > IM_LEN(im,i))
+ return (EOF)
+
+ # Check for buffered data.
+ if (XT_BUF(xt) != NULL) {
+ if (v[2] >= XT_VS(xt,2) && v[2] <= XT_VE(xt,2)) {
+ if (XT_BTYPE(xt) != TY_SHORT)
+ call error (1, "Cannot mix data types")
+ nc = IM_LEN(im,1)
+ buf = XT_BUF(xt) + (v[2]-XT_VS(xt,2)) * IM_LEN(im,1)
+ XT_FLAG(xt) = flag
+ if (i == 1)
+ v[1] = nc + 1
+ else
+ j = imloop (v, unit_v, IM_LEN(im,1), unit_v, i)
+ return (nc)
+ }
+ }
+
+ # Handle more images than the maximum that can be open at one time.
+ if (nopen >= max_openim) {
+ if (option == XT_MAPUNMAP || v[2] == 0) {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+
+ # Buffer some number of lines.
+ nl = XT_BUFSIZE(xt1) / sizeof (TY_SHORT) / IM_LEN(im,1)
+ if (nl > 1) {
+ nc = IM_LEN(im,1)
+ call amovl (v, XT_VS(xt1,1), IM_MAXDIM)
+ call amovl (v, XT_VE(xt1,1), IM_MAXDIM)
+ XT_VS(xt1,1) = 1
+ XT_VE(xt1,1) = nc
+ XT_VE(xt1,2) = min (XT_VS(xt1,2)+(nl-1), IM_LEN(im,2))
+ nl = XT_VE(xt1,2) - XT_VS(xt1,2) + 1
+ XT_BTYPE(xt1) = TY_SHORT
+ call malloc (XT_BUF(xt1), nl*nc, XT_BTYPE(xt1))
+ ptr = imggss (im, XT_VS(xt1,1), XT_VE(xt1,1),
+ IM_NDIM(im))
+ call amovs (Mems[ptr], Mems[XT_BUF(xt1)], nl*nc)
+ }
+
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ min_open = i + 1
+ break
+ }
+ if (index <= min_open)
+ min_open = index
+ else {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ if (XT_IM(xt1) == NULL)
+ next
+ min_open = i
+ break
+ }
+ }
+ } else {
+ # Check here because we can't catch error in immap.
+ i = open ("dev$null", READ_ONLY, BINARY_FILE)
+ call close (i)
+ if (i == LAST_FD - 1)
+ call error (SYS_FTOOMANYFILES, "Too many open files")
+ }
+ }
+
+ # Open image.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl immap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ im = immap (XT_IMNAME(xt), READ_ONLY, XT_ARG(xt))
+ XT_IM(xt) = im
+ call imseti (im, IM_BUFSIZE, XT_BUFSIZE(xt))
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+ nopen = nopen + 1
+ XT_CLOSEFD(xt) = YES
+ if (nopenpix < MAX_OPENPIX) {
+ XT_CLOSEFD(xt) = NO
+ nopenpix = nopenpix + 1
+ }
+ if (XT_CLOSEFD(xt) == YES)
+ call imseti (im, IM_CLOSEFD, YES)
+ XT_FLAG(xt) = flag
+
+ return (imgnls (im, buf, v))
+end
+
+# XT_IMGNL -- Return the next line for the indexed image.
+# Possibly unmap another image if too many files are open.
+# Buffer data when an image is unmmaped to minimize the mapping of images.
+# If the requested index has not been mapped use the default pointer.
+
+int procedure xt_imgnli (imdef, index, buf, v, flag)
+
+pointer imdef #I Default pointer
+int index #I index
+pointer buf #O Data buffer
+long v[ARB] #I Line vector
+int flag #I Flag (=output line)
+
+int i, j, nc, nl, open(), imgnli(), sizeof(), imloop()
+pointer im, xt, xt1, ptr, immap(), imggsi()
+errchk open, immap, imgnli, imggsi, imunmap
+
+long unit_v[IM_MAXDIM]
+data unit_v /IM_MAXDIM * 1/
+
+include "xtimmap.com"
+
+begin
+ # Get index pointer.
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ # Use default pointer if index has not been mapped.
+ if (xt == NULL)
+ return (imgnli (imdef, buf, v))
+
+ # Close images not accessed during previous line.
+ # In normal usage this should only occur once per line over all
+ # indexed images.
+ if (flag != last_flag) {
+ do i = 1, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL || XT_FLAG(xt1) == last_flag)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ call mfree (XT_BUF(xt1), XT_BTYPE(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ }
+
+ # Optimize the file I/O.
+ do i = nalloc, 1, -1 {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ if (nopenpix < MAX_OPENPIX) {
+ if (XT_CLOSEFD(xt1) == NO)
+ next
+ XT_CLOSEFD(xt1) = NO
+ call imseti (im, IM_CLOSEFD, NO)
+ nopenpix = nopenpix + 1
+ }
+ }
+ last_flag = flag
+ }
+
+ # Use IMIO for already opened images.
+ im = XT_IM(xt)
+ if (im != NULL) {
+ XT_FLAG(xt) = flag
+ return (imgnli (im, buf, v))
+ }
+
+ # If the image is not currently mapped use the stored header.
+ im = XT_HDR(xt)
+
+ # Check for EOF.
+ i = IM_NDIM(im)
+ if (v[i] > IM_LEN(im,i))
+ return (EOF)
+
+ # Check for buffered data.
+ if (XT_BUF(xt) != NULL) {
+ if (v[2] >= XT_VS(xt,2) && v[2] <= XT_VE(xt,2)) {
+ if (XT_BTYPE(xt) != TY_INT)
+ call error (1, "Cannot mix data types")
+ nc = IM_LEN(im,1)
+ buf = XT_BUF(xt) + (v[2]-XT_VS(xt,2)) * IM_LEN(im,1)
+ XT_FLAG(xt) = flag
+ if (i == 1)
+ v[1] = nc + 1
+ else
+ j = imloop (v, unit_v, IM_LEN(im,1), unit_v, i)
+ return (nc)
+ }
+ }
+
+ # Handle more images than the maximum that can be open at one time.
+ if (nopen >= max_openim) {
+ if (option == XT_MAPUNMAP || v[2] == 0) {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+
+ # Buffer some number of lines.
+ nl = XT_BUFSIZE(xt1) / sizeof (TY_INT) / IM_LEN(im,1)
+ if (nl > 1) {
+ nc = IM_LEN(im,1)
+ call amovl (v, XT_VS(xt1,1), IM_MAXDIM)
+ call amovl (v, XT_VE(xt1,1), IM_MAXDIM)
+ XT_VS(xt1,1) = 1
+ XT_VE(xt1,1) = nc
+ XT_VE(xt1,2) = min (XT_VS(xt1,2)+(nl-1), IM_LEN(im,2))
+ nl = XT_VE(xt1,2) - XT_VS(xt1,2) + 1
+ XT_BTYPE(xt1) = TY_INT
+ call malloc (XT_BUF(xt1), nl*nc, XT_BTYPE(xt1))
+ ptr = imggsi (im, XT_VS(xt1,1), XT_VE(xt1,1),
+ IM_NDIM(im))
+ call amovi (Memi[ptr], Memi[XT_BUF(xt1)], nl*nc)
+ }
+
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ min_open = i + 1
+ break
+ }
+ if (index <= min_open)
+ min_open = index
+ else {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ if (XT_IM(xt1) == NULL)
+ next
+ min_open = i
+ break
+ }
+ }
+ } else {
+ # Check here because we can't catch error in immap.
+ i = open ("dev$null", READ_ONLY, BINARY_FILE)
+ call close (i)
+ if (i == LAST_FD - 1)
+ call error (SYS_FTOOMANYFILES, "Too many open files")
+ }
+ }
+
+ # Open image.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl immap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ im = immap (XT_IMNAME(xt), READ_ONLY, XT_ARG(xt))
+ XT_IM(xt) = im
+ call imseti (im, IM_BUFSIZE, XT_BUFSIZE(xt))
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+ nopen = nopen + 1
+ XT_CLOSEFD(xt) = YES
+ if (nopenpix < MAX_OPENPIX) {
+ XT_CLOSEFD(xt) = NO
+ nopenpix = nopenpix + 1
+ }
+ if (XT_CLOSEFD(xt) == YES)
+ call imseti (im, IM_CLOSEFD, YES)
+ XT_FLAG(xt) = flag
+
+ return (imgnli (im, buf, v))
+end
+
+# XT_IMGNL -- Return the next line for the indexed image.
+# Possibly unmap another image if too many files are open.
+# Buffer data when an image is unmmaped to minimize the mapping of images.
+# If the requested index has not been mapped use the default pointer.
+
+int procedure xt_imgnlr (imdef, index, buf, v, flag)
+
+pointer imdef #I Default pointer
+int index #I index
+pointer buf #O Data buffer
+long v[ARB] #I Line vector
+int flag #I Flag (=output line)
+
+int i, j, nc, nl, open(), imgnlr(), sizeof(), imloop()
+pointer im, xt, xt1, ptr, immap(), imggsr()
+errchk open, immap, imgnlr, imggsr, imunmap
+
+long unit_v[IM_MAXDIM]
+data unit_v /IM_MAXDIM * 1/
+
+include "xtimmap.com"
+
+begin
+ # Get index pointer.
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ # Use default pointer if index has not been mapped.
+ if (xt == NULL)
+ return (imgnlr (imdef, buf, v))
+
+ # Close images not accessed during previous line.
+ # In normal usage this should only occur once per line over all
+ # indexed images.
+ if (flag != last_flag) {
+ do i = 1, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL || XT_FLAG(xt1) == last_flag)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ call mfree (XT_BUF(xt1), XT_BTYPE(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ }
+
+ # Optimize the file I/O.
+ do i = nalloc, 1, -1 {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ if (nopenpix < MAX_OPENPIX) {
+ if (XT_CLOSEFD(xt1) == NO)
+ next
+ XT_CLOSEFD(xt1) = NO
+ call imseti (im, IM_CLOSEFD, NO)
+ nopenpix = nopenpix + 1
+ }
+ }
+ last_flag = flag
+ }
+
+ # Use IMIO for already opened images.
+ im = XT_IM(xt)
+ if (im != NULL) {
+ XT_FLAG(xt) = flag
+ return (imgnlr (im, buf, v))
+ }
+
+ # If the image is not currently mapped use the stored header.
+ im = XT_HDR(xt)
+
+ # Check for EOF.
+ i = IM_NDIM(im)
+ if (v[i] > IM_LEN(im,i))
+ return (EOF)
+
+ # Check for buffered data.
+ if (XT_BUF(xt) != NULL) {
+ if (v[2] >= XT_VS(xt,2) && v[2] <= XT_VE(xt,2)) {
+ if (XT_BTYPE(xt) != TY_REAL)
+ call error (1, "Cannot mix data types")
+ nc = IM_LEN(im,1)
+ buf = XT_BUF(xt) + (v[2]-XT_VS(xt,2)) * IM_LEN(im,1)
+ XT_FLAG(xt) = flag
+ if (i == 1)
+ v[1] = nc + 1
+ else
+ j = imloop (v, unit_v, IM_LEN(im,1), unit_v, i)
+ return (nc)
+ }
+ }
+
+ # Handle more images than the maximum that can be open at one time.
+ if (nopen >= max_openim) {
+ if (option == XT_MAPUNMAP || v[2] == 0) {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+
+ # Buffer some number of lines.
+ nl = XT_BUFSIZE(xt1) / sizeof (TY_REAL) / IM_LEN(im,1)
+ if (nl > 1) {
+ nc = IM_LEN(im,1)
+ call amovl (v, XT_VS(xt1,1), IM_MAXDIM)
+ call amovl (v, XT_VE(xt1,1), IM_MAXDIM)
+ XT_VS(xt1,1) = 1
+ XT_VE(xt1,1) = nc
+ XT_VE(xt1,2) = min (XT_VS(xt1,2)+(nl-1), IM_LEN(im,2))
+ nl = XT_VE(xt1,2) - XT_VS(xt1,2) + 1
+ XT_BTYPE(xt1) = TY_REAL
+ call malloc (XT_BUF(xt1), nl*nc, XT_BTYPE(xt1))
+ ptr = imggsr (im, XT_VS(xt1,1), XT_VE(xt1,1),
+ IM_NDIM(im))
+ call amovr (Memr[ptr], Memr[XT_BUF(xt1)], nl*nc)
+ }
+
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ min_open = i + 1
+ break
+ }
+ if (index <= min_open)
+ min_open = index
+ else {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ if (XT_IM(xt1) == NULL)
+ next
+ min_open = i
+ break
+ }
+ }
+ } else {
+ # Check here because we can't catch error in immap.
+ i = open ("dev$null", READ_ONLY, BINARY_FILE)
+ call close (i)
+ if (i == LAST_FD - 1)
+ call error (SYS_FTOOMANYFILES, "Too many open files")
+ }
+ }
+
+ # Open image.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl immap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ im = immap (XT_IMNAME(xt), READ_ONLY, XT_ARG(xt))
+ XT_IM(xt) = im
+ call imseti (im, IM_BUFSIZE, XT_BUFSIZE(xt))
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+ nopen = nopen + 1
+ XT_CLOSEFD(xt) = YES
+ if (nopenpix < MAX_OPENPIX) {
+ XT_CLOSEFD(xt) = NO
+ nopenpix = nopenpix + 1
+ }
+ if (XT_CLOSEFD(xt) == YES)
+ call imseti (im, IM_CLOSEFD, YES)
+ XT_FLAG(xt) = flag
+
+ return (imgnlr (im, buf, v))
+end
+
+# XT_IMGNL -- Return the next line for the indexed image.
+# Possibly unmap another image if too many files are open.
+# Buffer data when an image is unmmaped to minimize the mapping of images.
+# If the requested index has not been mapped use the default pointer.
+
+int procedure xt_imgnld (imdef, index, buf, v, flag)
+
+pointer imdef #I Default pointer
+int index #I index
+pointer buf #O Data buffer
+long v[ARB] #I Line vector
+int flag #I Flag (=output line)
+
+int i, j, nc, nl, open(), imgnld(), sizeof(), imloop()
+pointer im, xt, xt1, ptr, immap(), imggsd()
+errchk open, immap, imgnld, imggsd, imunmap
+
+long unit_v[IM_MAXDIM]
+data unit_v /IM_MAXDIM * 1/
+
+include "xtimmap.com"
+
+begin
+ # Get index pointer.
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ # Use default pointer if index has not been mapped.
+ if (xt == NULL)
+ return (imgnld (imdef, buf, v))
+
+ # Close images not accessed during previous line.
+ # In normal usage this should only occur once per line over all
+ # indexed images.
+ if (flag != last_flag) {
+ do i = 1, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL || XT_FLAG(xt1) == last_flag)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ call mfree (XT_BUF(xt1), XT_BTYPE(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ }
+
+ # Optimize the file I/O.
+ do i = nalloc, 1, -1 {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ if (nopenpix < MAX_OPENPIX) {
+ if (XT_CLOSEFD(xt1) == NO)
+ next
+ XT_CLOSEFD(xt1) = NO
+ call imseti (im, IM_CLOSEFD, NO)
+ nopenpix = nopenpix + 1
+ }
+ }
+ last_flag = flag
+ }
+
+ # Use IMIO for already opened images.
+ im = XT_IM(xt)
+ if (im != NULL) {
+ XT_FLAG(xt) = flag
+ return (imgnld (im, buf, v))
+ }
+
+ # If the image is not currently mapped use the stored header.
+ im = XT_HDR(xt)
+
+ # Check for EOF.
+ i = IM_NDIM(im)
+ if (v[i] > IM_LEN(im,i))
+ return (EOF)
+
+ # Check for buffered data.
+ if (XT_BUF(xt) != NULL) {
+ if (v[2] >= XT_VS(xt,2) && v[2] <= XT_VE(xt,2)) {
+ if (XT_BTYPE(xt) != TY_DOUBLE)
+ call error (1, "Cannot mix data types")
+ nc = IM_LEN(im,1)
+ buf = XT_BUF(xt) + (v[2]-XT_VS(xt,2)) * IM_LEN(im,1)
+ XT_FLAG(xt) = flag
+ if (i == 1)
+ v[1] = nc + 1
+ else
+ j = imloop (v, unit_v, IM_LEN(im,1), unit_v, i)
+ return (nc)
+ }
+ }
+
+ # Handle more images than the maximum that can be open at one time.
+ if (nopen >= max_openim) {
+ if (option == XT_MAPUNMAP || v[2] == 0) {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+
+ # Buffer some number of lines.
+ nl = XT_BUFSIZE(xt1) / sizeof (TY_DOUBLE) / IM_LEN(im,1)
+ if (nl > 1) {
+ nc = IM_LEN(im,1)
+ call amovl (v, XT_VS(xt1,1), IM_MAXDIM)
+ call amovl (v, XT_VE(xt1,1), IM_MAXDIM)
+ XT_VS(xt1,1) = 1
+ XT_VE(xt1,1) = nc
+ XT_VE(xt1,2) = min (XT_VS(xt1,2)+(nl-1), IM_LEN(im,2))
+ nl = XT_VE(xt1,2) - XT_VS(xt1,2) + 1
+ XT_BTYPE(xt1) = TY_DOUBLE
+ call malloc (XT_BUF(xt1), nl*nc, XT_BTYPE(xt1))
+ ptr = imggsd (im, XT_VS(xt1,1), XT_VE(xt1,1),
+ IM_NDIM(im))
+ call amovd (Memd[ptr], Memd[XT_BUF(xt1)], nl*nc)
+ }
+
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ min_open = i + 1
+ break
+ }
+ if (index <= min_open)
+ min_open = index
+ else {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ if (XT_IM(xt1) == NULL)
+ next
+ min_open = i
+ break
+ }
+ }
+ } else {
+ # Check here because we can't catch error in immap.
+ i = open ("dev$null", READ_ONLY, BINARY_FILE)
+ call close (i)
+ if (i == LAST_FD - 1)
+ call error (SYS_FTOOMANYFILES, "Too many open files")
+ }
+ }
+
+ # Open image.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl immap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ im = immap (XT_IMNAME(xt), READ_ONLY, XT_ARG(xt))
+ XT_IM(xt) = im
+ call imseti (im, IM_BUFSIZE, XT_BUFSIZE(xt))
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+ nopen = nopen + 1
+ XT_CLOSEFD(xt) = YES
+ if (nopenpix < MAX_OPENPIX) {
+ XT_CLOSEFD(xt) = NO
+ nopenpix = nopenpix + 1
+ }
+ if (XT_CLOSEFD(xt) == YES)
+ call imseti (im, IM_CLOSEFD, YES)
+ XT_FLAG(xt) = flag
+
+ return (imgnld (im, buf, v))
+end
+
diff --git a/pkg/images/immatch/src/imcombine/src/icaclip.gx b/pkg/images/immatch/src/imcombine/src/icaclip.gx
new file mode 100644
index 00000000..de3b04d6
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icaclip.gx
@@ -0,0 +1,575 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 3 # Minimum number of images for this algorithm
+
+$for (sird)
+# IC_AAVSIGCLIP -- Reject pixels using an average sigma about the average
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_aavsigclip$t (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+$if (datatype == sil)
+real average[npts] # Average
+$else
+PIXEL average[npts] # Average
+$endif
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+$if (datatype == sil)
+real d1, low, high, sum, a, s, s1, r, one
+data one /1.0/
+$else
+PIXEL d1, low, high, sum, a, s, s1, r, one
+data one /1$f/
+$endif
+pointer sp, sums, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (sums, npts, TY_REAL)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Since the unweighted average is computed here possibly skip combining
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Compute the unweighted average with the high and low rejected and
+ # the poisson scaled average sigma. There must be at least three
+ # pixels at each point to define the average and contributions to
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ nin = max (0, n[1])
+ s = 0.
+ n2 = 0
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3)
+ next
+
+ # Unweighted average with the high and low rejected
+ low = Mem$t[d[1]+k]
+ high = Mem$t[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Mem$t[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Mem$t[dp1]
+ l = Memi[mp1]
+ s1 = max (one, (a + zeros[l]) / scales[l])
+ s = s + (d1 - a) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, a)
+ do j = 1, n1
+ s = s + (Mem$t[d[j]+k] - a) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the average and sum for later.
+ average[i] = a
+ Memr[sums+k] = sum
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ s = sqrt (s / (n2 - 1))
+
+ # Reject pixels and compute the final average (if needed).
+ # There must be at least three pixels at each point for rejection.
+ # Iteratively scale the mean sigma and reject pixels
+ # Compact the data and keep track of the image IDs if needed.
+
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (2, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Mem$t[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mem$t[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ a = average[i]
+ sum = Memr[sums+k]
+
+ repeat {
+ n2 = n1
+ if (s > 0.) {
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ d1 = Mem$t[dp1]
+ l = Memi[mp1]
+ s1 = s * sqrt (max (one, (a+zeros[l]) / scales[l]))
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ s1 = s * sqrt (max (one, a))
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Mem$t[dp1]
+ r = (d1 - a) / s1
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mem$t[dp1]
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Mem$t[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mem$t[dp1]
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Mem$t[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MAVSIGCLIP -- Reject pixels using an average sigma about the median
+# The average sigma is normalized by the expected poisson sigma.
+
+procedure ic_mavsigclip$t (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+$if (datatype == sil)
+real median[npts] # Median
+$else
+PIXEL median[npts] # Median
+$endif
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+pointer sp, resid, mp1, mp2
+$if (datatype == sil)
+real med, low, high, sig, r, s, s1, one
+data one /1.0/
+$else
+PIXEL med, low, high, sig, r, s, s1, one
+data one /1$f/
+$endif
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute the poisson scaled average sigma about the median.
+ # There must be at least three pixels at each point to define
+ # the mean sigma. Corrections for differences in the image
+ # scale factors are selected by the doscale1 flag.
+
+ s = 0.
+ n2 = 0
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (n1 < 3) {
+ if (n1 == 0)
+ median[i] = blank
+ else if (n1 == 1)
+ median[i] = Mem$t[d[1]+k]
+ else {
+ low = Mem$t[d[1]+k]
+ high = Mem$t[d[2]+k]
+ median[i] = (low + high) / 2.
+ }
+ next
+ }
+
+ # Median
+ n3 = 1 + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Mem$t[d[n3-1]+k]
+ high = Mem$t[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Mem$t[d[n3]+k]
+
+ # Poisson scaled sigma accumulation
+ if (doscale1) {
+ do j = 1, n1 {
+ l = Memi[m[j]+k]
+ s1 = max (one, (med + zeros[l]) / scales[l])
+ s = s + (Mem$t[d[j]+k] - med) ** 2 / s1
+ }
+ } else {
+ s1 = max (one, med)
+ do j = 1, n1
+ s = s + (Mem$t[d[j]+k] - med) ** 2 / s1
+ }
+ n2 = n2 + n1
+
+ # Save the median for later.
+ median[i] = med
+ }
+
+ # Here is the final sigma.
+ if (n2 > 1)
+ sig = sqrt (s / (n2 - 1))
+ else {
+ call sfree (sp)
+ return
+ }
+
+ # Compute individual sigmas and iteratively clip.
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 < max (3, maxkeep+1))
+ next
+ nl = 1
+ nh = n1
+ med = median[i]
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 >= max (MINCLIP, maxkeep+1) && sig > 0.) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (med - Mem$t[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s1 = sig * sqrt (max (one, (med+zeros[l])/scales[l]))
+ r = (Mem$t[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ s1 = sig * sqrt (max (one, med))
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Mem$t[d[nl]+k]) / s1
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Mem$t[d[nh]+k] - med) / s1
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Mem$t[d[n3-1]+k]
+ high = Mem$t[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Mem$t[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many are rejected add some back in.
+ # Pixels with equal residuals are added together.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+
+ # Recompute median
+ if (n1 < n2) {
+ if (n1 > 0) {
+ n3 = nl + n1 / 2
+ if (mod (n1, 2) == 0) {
+ low = Mem$t[d[n3-1]+k]
+ high = Mem$t[d[n3]+k]
+ med = (low + high) / 2.
+ } else
+ med = Mem$t[d[n3]+k]
+ } else
+ med = blank
+ }
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mem$t[d[l]+k] = Mem$t[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mem$t[d[l]+k] = Mem$t[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icaverage.gx b/pkg/images/immatch/src/imcombine/src/icaverage.gx
new file mode 100644
index 00000000..a474bb9d
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icaverage.gx
@@ -0,0 +1,120 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+include "../icmask.h"
+
+$for (sird)
+# IC_AVERAGE -- Compute the average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_average$t (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+$if (datatype == sil)
+real average[npts] # Average (returned)
+$else
+PIXEL average[npts] # Average (returned)
+$endif
+
+int i, j, k, n1
+real sumwt, wt
+$if (datatype == sil)
+real sum
+$else
+PIXEL sum
+$endif
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Mem$t[d[1]+k] * wt
+ do j = 2, n[i] {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Mem$t[d[j]+k] * wt
+ }
+ average[i] = sum
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ sum = Mem$t[d[1]+k]
+ do j = 2, n[i]
+ sum = sum + Mem$t[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n[i]
+ else
+ average[i] = sum
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ wt = wts[Memi[m[1]+k]]
+ sum = Mem$t[d[1]+k] * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + Mem$t[d[j]+k] * wt
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sum / sumwt
+ else {
+ sum = Mem$t[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mem$t[d[j]+k]
+ average[i] = sum / n1
+ }
+ } else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ sum = Mem$t[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mem$t[d[j]+k]
+ if (doaverage == YES)
+ average[i] = sum / n1
+ else
+ average[i] = sum
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/iccclip.gx b/pkg/images/immatch/src/imcombine/src/iccclip.gx
new file mode 100644
index 00000000..5b1b724e
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/iccclip.gx
@@ -0,0 +1,471 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 2 # Mininum number of images for algorithm
+
+$for (sird)
+# IC_ACCDCLIP -- Reject pixels using CCD noise parameters about the average
+
+procedure ic_accdclip$t (d, m, n, scales, zeros, nm, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model parameters
+int nimages # Number of images
+int npts # Number of output points per line
+$if (datatype == sil)
+real average[npts] # Average
+$else
+PIXEL average[npts] # Average
+$endif
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+$if (datatype == sil)
+real d1, low, high, sum, a, s, r, zero
+data zero /0.0/
+$else
+PIXEL d1, low, high, sum, a, s, r, zero
+data zero /0$f/
+$endif
+pointer sp, resid, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are no pixels go on to the combining. Since the unweighted
+ # average is computed here possibly skip the combining later.
+
+ # There must be at least max (1, nkeep) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ } else if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # There must be at least two pixels for rejection. The initial
+ # average is the low/high rejected average except in the case of
+ # just two pixels. The rejections are iterated and the average
+ # is recomputed. Corrections for scaling may be performed.
+ # Depending on other flags the image IDs may also need to be adjusted.
+
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 <= max (MINCLIP-1, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Mem$t[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mem$t[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ repeat {
+ if (n1 == 2) {
+ sum = Mem$t[d[1]+k]
+ sum = sum + Mem$t[d[2]+k]
+ a = sum / 2
+ } else {
+ low = Mem$t[d[1]+k]
+ high = Mem$t[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Mem$t[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+ }
+ n2 = n1
+ if (doscale1) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+
+ l = Memi[mp1]
+ s = scales[l]
+ d1 = max (zero, s * (a + zeros[l]))
+ s = sqrt (nm[1,l] + d1/nm[2,l] + (d1*nm[3,l])**2) / s
+
+ d1 = Mem$t[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ mp2 = m[n1] + k
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, a)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (j=1; j<=n1; j=j+1) {
+ if (keepids) {
+ l = Memi[m[j]+k]
+ s = max (zero, a)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ dp1 = d[j] + k
+ d1 = Mem$t[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs(r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mem$t[dp1]
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Mem$t[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mem$t[dp1]
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Mem$t[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+ }
+
+ n[i] = n1
+ if (!docombine)
+ if (n1 > 0)
+ average[i] = sum / n1
+ else
+ average[i] = blank
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_CCDCLIP -- Reject pixels using CCD noise parameters about the median
+
+procedure ic_mccdclip$t (d, m, n, scales, zeros, nm, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real nm[3,nimages] # Noise model
+int nimages # Number of images
+int npts # Number of output points per line
+$if (datatype == sil)
+real median[npts] # Median
+$else
+PIXEL median[npts] # Median
+$endif
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, mp1, mp2
+$if (datatype == sil)
+real med, zero
+data zero /0.0/
+$else
+PIXEL med, zero
+data zero /0$f/
+$endif
+
+include "../icombine.com"
+
+begin
+ # There must be at least max (MINCLIP, nkeep+1) pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0) {
+ med = Mem$t[d[n3-1]+k]
+ med = (med + Mem$t[d[n3]+k]) / 2.
+ } else
+ med = Mem$t[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ for (; nl <= nh; nl = nl + 1) {
+ l = Memi[m[nl]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (med - Mem$t[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ l = Memi[m[nh]+k]
+ s = scales[l]
+ r = max (zero, s * (med + zeros[l]))
+ s = sqrt (nm[1,l] + r/nm[2,l] + (r*nm[3,l])**2) / s
+ r = (Mem$t[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ } else {
+ if (!keepids) {
+ s = max (zero, med)
+ s = sqrt (nm[1,1] + s/nm[2,1] + (s*nm[3,1])**2)
+ }
+ for (; nl <= nh; nl = nl + 1) {
+ if (keepids) {
+ l = Memi[m[nl]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (med - Mem$t[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ if (keepids) {
+ l = Memi[m[nh]+k]
+ s = max (zero, med)
+ s = sqrt (nm[1,l] + s/nm[2,l] + (s*nm[3,l])**2)
+ }
+ r = (Mem$t[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mem$t[d[l]+k] = Mem$t[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mem$t[d[l]+k] = Mem$t[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median is computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icemask.x b/pkg/images/immatch/src/imcombine/src/icemask.x
new file mode 100644
index 00000000..e29edd5e
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icemask.x
@@ -0,0 +1,115 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+
+
+# IC_EMASK -- Create exposure mask.
+
+procedure ic_emask (pm, v, id, nimages, n, wts, npts)
+
+pointer pm #I Pixel mask
+long v[ARB] #I Output vector
+pointer id[nimages] #I Image id pointers
+int nimages #I Number of images
+int n[npts] #I Number of good pixels
+real wts[npts] #I Weights
+int npts #I Number of output pixels per line
+
+int i, j, k, impnli()
+real exp
+pointer buf
+errchk impnli
+
+pointer exps # Exposure times
+pointer ev # IMIO coordinate vector
+real ezero # Integer to real zero
+real escale # Integer to real scale
+int einit # Initialization flag
+common /emask/ exps, ev, ezero, escale, einit
+
+begin
+ # Write scaling factors to the header.
+ if (einit == NO) {
+ if (ezero != 0. || escale != 1.) {
+ call imaddr (pm, "MASKZERO", ezero)
+ call imaddr (pm, "MASKSCAL", escale)
+ }
+ einit = YES
+ }
+
+ call amovl (v, Meml[ev], IM_MAXDIM)
+ i = impnli (pm, buf, Meml[ev])
+ call aclri (Memi[buf], npts)
+ do i = 1, npts {
+ exp = 0.
+ do j = 1, n[i] {
+ k = Memi[id[j]+i-1]
+ if (wts[k] > 0.)
+ exp = exp + Memr[exps+k-1]
+ }
+ Memi[buf] = nint((exp-ezero)/escale)
+ buf = buf + 1
+ }
+end
+
+
+# IC_EINIT -- Initialize exposure mask.
+
+procedure ic_einit (in, nimages, key, default, maxval)
+
+int in[nimages] #I Image pointers
+int nimages #I Number of images
+char key[ARB] #I Exposure time keyword
+real default #I Default exposure time
+int maxval #I Maximum mask value
+
+int i
+real exp, emin, emax, efrac, imgetr()
+
+pointer exps # Exposure times
+pointer ev # IMIO coordinate vector
+real ezero # Integer to real zero
+real escale # Integer to real scale
+int einit # Initialization flag
+common /emask/ exps, ev, ezero, escale, einit
+
+begin
+ call malloc (ev, IM_MAXDIM, TY_LONG)
+ call malloc (exps, nimages, TY_REAL)
+
+ emax = 0.
+ emin = MAX_REAL
+ efrac = 0
+ do i = 1, nimages {
+ iferr (exp = imgetr (in[i], key))
+ exp = default
+ exp = max (0., exp)
+ emax = emax + exp
+ if (exp > 0.)
+ emin = min (exp, emin)
+ efrac = max (abs(exp-nint(exp)), efrac)
+ Memr[exps+i-1] = exp
+ }
+
+ # Set scaling.
+ ezero = 0.
+ escale = 1.
+ if (emin < 1.) {
+ escale = emin
+ emin = emin / escale
+ emax = emax / escale
+ } else if (emin == MAX_REAL)
+ emin = 0.
+ if (efrac > 0.001 && emax-emin < 1000.) {
+ escale = escale / 1000.
+ emin = emin * 1000.
+ emax = emax * 1000.
+ }
+ while (emax > maxval) {
+ escale = escale * 10.
+ emin = emin / 10.
+ emax = emax / 10.
+ }
+ einit = NO
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icgdata.gx b/pkg/images/immatch/src/imcombine/src/icgdata.gx
new file mode 100644
index 00000000..a05f5646
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icgdata.gx
@@ -0,0 +1,396 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+
+$for (sird)
+# IC_GDATA -- Get line of image and mask data and apply threshold and scaling.
+# Entirely empty lines are excluded. The data are compacted within the
+# input data buffers. If it is required, the connection to the original
+# image index is kept in the returned m data pointers.
+
+procedure ic_gdata$t (in, out, dbuf, d, id, n, m, lflag, offsets, scales,
+ zeros, nimages, npts, v1, v2)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+pointer dbuf[nimages] # Data buffers
+pointer d[nimages] # Data pointers
+pointer id[nimages] # ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Empty mask flags
+int offsets[nimages,ARB] # Image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+int nimages # Number of input images
+int npts # NUmber of output points per line
+long v1[ARB], v2[ARB] # Line vectors
+
+PIXEL temp
+int i, j, k, l, n1, n2, npix, nin, nout, ndim, nused, mtype, xt_imgnl$t()
+real a, b
+pointer buf, dp, ip, mp
+errchk xt_cpix, xt_imgnl$t
+
+PIXEL max_pixel
+$if (datatype == s)
+data max_pixel/MAX_SHORT/
+$else $if (datatype == i)
+data max_pixel/MAX_INT/
+$else $if (datatype == r)
+data max_pixel/MAX_REAL/
+$else
+data max_pixel/MAX_DOUBLE/
+$endif $endif $endif
+
+include "../icombine.com"
+
+begin
+ # Get masks and return if there is no data
+ call ic_mget (in, out, offsets, v1, v2, m, lflag, nimages, mtype)
+ if (dflag == D_NONE) {
+ call aclri (n, npts)
+ return
+ }
+
+ # Close images which are not needed.
+ nout = IM_LEN(out[1],1)
+ ndim = IM_NDIM(out[1])
+ if (!project && ndim < 3) {
+ do i = 1, nimages {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1)
+ call xt_cpix (i)
+ if (ndim > 1) {
+ j = v1[2] - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+ }
+ }
+
+ # Get data and fill data buffers. Correct for offsets if needed.
+ do i = 1, nimages {
+ if (lflag[i] == D_NONE)
+ next
+ if (dbuf[i] == NULL) {
+ call amovl (v1, v2, IM_MAXDIM)
+ if (project)
+ v2[ndim+1] = i
+ j = xt_imgnl$t (in[i], i, d[i], v2, v1[2])
+ } else {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1) {
+ lflag[i] = D_NONE
+ next
+ }
+ k = 1 + j - offsets[i,1]
+ v2[1] = k
+ do l = 2, ndim {
+ v2[l] = v1[l] - offsets[i,l]
+ if (v2[l] < 1 || v2[l] > IM_LEN(in[i],l)) {
+ lflag[i] = D_NONE
+ break
+ }
+ }
+ if (lflag[i] == D_NONE)
+ next
+ if (project)
+ v2[ndim+1] = i
+ l = xt_imgnl$t (in[i], i, buf, v2, v1[2])
+ call amov$t (Mem$t[buf+k-1], Mem$t[dbuf[i]+j], npix)
+ d[i] = dbuf[i]
+ }
+ }
+
+ # Set values to max_pixel if needed.
+ if (mtype == M_NOVAL) {
+ do i = 1, nimages {
+ dp = d[i]; mp = m[i]
+ if (lflag[i] == D_NONE || dp == NULL)
+ next
+ else if (lflag[i] == D_MIX) {
+ do j = 1, npts {
+ if (Memi[mp] == 1)
+ Mem$t[dp] = max_pixel
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply threshold if needed
+ if (dothresh) {
+ do i = 1, nimages {
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ a = Mem$t[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+
+ lflag[i] = D_MIX
+ dflag = D_MIX
+ }
+ dp = dp + 1
+ }
+
+ # Check for completely empty lines
+ if (lflag[i] == D_MIX) {
+ lflag[i] = D_NONE
+ mp = m[i]
+ do j = 1, npts {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1) {
+ a = Mem$t[dp]
+ if (a < lthresh || a > hthresh) {
+ if (mtype == M_NOVAL)
+ Memi[m[i]+j-1] = 2
+ else
+ Memi[m[i]+j-1] = 1
+ dflag = D_MIX
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+
+ # Check for completely empty lines
+ lflag[i] = D_NONE
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ lflag[i] = D_MIX
+ break
+ }
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Apply scaling (avoiding masked pixels which might overflow?)
+ if (doscale) {
+ if (dflag == D_ALL) {
+ do i = 1, nimages {
+ dp = d[i]
+ a = scales[i]
+ b = -zeros[i]
+ do j = 1, npts {
+ Mem$t[dp] = Mem$t[dp] / a + b
+ dp = dp + 1
+ }
+ }
+ } else if (dflag == D_MIX) {
+ do i = 1, nimages {
+ a = scales[i]
+ b = -zeros[i]
+ if (lflag[i] == D_ALL) {
+ dp = d[i]
+ do j = 1, npts {
+ Mem$t[dp] = Mem$t[dp] / a + b
+ dp = dp + 1
+ }
+ } else if (lflag[i] == D_MIX) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] != 1)
+ Mem$t[dp] = Mem$t[dp] / a + b
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+ }
+
+ # Sort pointers to exclude unused images.
+ # Use the lflag array to keep track of the image index.
+
+ if (dflag == D_ALL)
+ nused = nimages
+ else {
+ nused = 0
+ do i = 1, nimages {
+ if (lflag[i] != D_NONE) {
+ nused = nused + 1
+ d[nused] = d[i]
+ m[nused] = m[i]
+ lflag[nused] = i
+ }
+ }
+ do i = nused+1, nimages
+ d[i] = NULL
+ if (nused == 0)
+ dflag = D_NONE
+ }
+
+ # Compact data to remove bad pixels
+ # Keep track of the image indices if needed
+ # If growing mark the end of the included image indices with zero
+
+ if (dflag == D_ALL) {
+ call amovki (nused, n, npts)
+ if (keepids)
+ do i = 1, nimages
+ call amovki (i, Memi[id[i]], npts)
+ } else if (dflag == D_NONE)
+ call aclri (n, npts)
+ else {
+ call aclri (n, npts)
+ if (keepids) {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ ip = id[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ Memi[ip] = l
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Mem$t[d[k]+j-1]
+ Mem$t[d[k]+j-1] = Mem$t[dp]
+ Mem$t[dp] = temp
+ Memi[ip] = Memi[id[k]+j-1]
+ Memi[id[k]+j-1] = l
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ } else
+ Memi[ip] = 0
+ dp = dp + 1
+ ip = ip + 1
+ mp = mp + 1
+ }
+ }
+ if (grow >= 1.) {
+ do j = 1, npts {
+ do i = n[j]+1, nimages
+ Memi[id[i]+j-1] = 0
+ }
+ }
+ } else {
+ do i = 1, nused {
+ l = lflag[i]
+ nin = IM_LEN(in[l],1)
+ j = max (0, offsets[l,1])
+ k = min (nout, nin + offsets[l,1])
+ npix = k - j
+ n1 = 1 + j
+ n2 = n1 + npix - 1
+ dp = d[i] + n1 - 1
+ mp = m[i] + n1 - 1
+ do j = n1, n2 {
+ if (Memi[mp] == 0) {
+ n[j] = n[j] + 1
+ k = n[j]
+ if (k < i) {
+ temp = Mem$t[d[k]+j-1]
+ Mem$t[d[k]+j-1] = Mem$t[dp]
+ Mem$t[dp] = temp
+ Memi[mp] = Memi[m[k]+j-1]
+ Memi[m[k]+j-1] = 0
+ }
+ }
+ dp = dp + 1
+ mp = mp + 1
+ }
+ }
+ }
+ }
+
+ # Sort the pixels and IDs if needed
+ if (mclip) {
+ call malloc (dp, nused, TY_PIXEL)
+ if (keepids) {
+ call malloc (ip, nused, TY_INT)
+ call ic_2sort$t (d, Mem$t[dp], id, Memi[ip], n, npts)
+ call mfree (ip, TY_INT)
+ } else
+ call ic_sort$t (d, Mem$t[dp], n, npts)
+ call mfree (dp, TY_PIXEL)
+ }
+
+ # If no good pixels set the number of usable values as -n and
+ # shift them to lower values.
+ if (mtype == M_NOVAL) {
+ if (keepids) {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ ip = id[i] + j - 1
+ if (Mem$t[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i) {
+ Mem$t[d[k]+j-1] = Mem$t[dp]
+ Memi[id[k]+j-1] = Memi[ip]
+ }
+ }
+ }
+ }
+ } else {
+ do j = 1, npts {
+ if (n[j] > 0)
+ next
+ n[j] = 0
+ do i = 1, nused {
+ dp = d[i] + j - 1
+ if (Mem$t[dp] < max_pixel) {
+ n[j] = n[j] - 1
+ k = -n[j]
+ if (k < i)
+ Mem$t[d[k]+j-1] = Mem$t[dp]
+ }
+ }
+ }
+ }
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icgrow.gx b/pkg/images/immatch/src/imcombine/src/icgrow.gx
new file mode 100644
index 00000000..caf7dd29
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icgrow.gx
@@ -0,0 +1,135 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <pmset.h>
+include "../icombine.h"
+
+# IC_GROW -- Mark neigbors of rejected pixels.
+# The rejected pixels (original plus grown) are saved in pixel masks.
+
+procedure ic_grow (out, v, m, n, buf, nimages, npts, pms)
+
+pointer out # Output image pointer
+long v[ARB] # Output vector
+pointer m[ARB] # Image id pointers
+int n[ARB] # Number of good pixels
+int buf[npts,nimages] # Working buffer
+int nimages # Number of images
+int npts # Number of output points per line
+pointer pms # Pointer to array of pixel masks
+
+int i, j, k, l, line, nl, rop, igrow, nset, ncompress, or()
+real grow2, i2
+pointer mp, pm, pm_newmask()
+errchk pm_newmask()
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_NONE || grow == 0.)
+ return
+
+ line = v[2]
+ nl = IM_LEN(out,2)
+ rop = or (PIX_SRC, PIX_DST)
+
+ igrow = grow
+ grow2 = grow**2
+ do l = 0, igrow {
+ i2 = grow2 - l * l
+ call aclri (buf, npts*nimages)
+ nset = 0
+ do j = 1, npts {
+ do k = n[j]+1, nimages {
+ mp = Memi[m[k]+j-1]
+ if (mp == 0)
+ next
+ do i = 0, igrow {
+ if (i**2 > i2)
+ next
+ if (j > i)
+ buf[j-i,mp] = 1
+ if (j+i <= npts)
+ buf[j+i,mp] = 1
+ nset = nset + 1
+ }
+ }
+ }
+ if (nset == 0)
+ return
+
+ if (pms == NULL) {
+ call malloc (pms, nimages, TY_POINTER)
+ do i = 1, nimages
+ Memi[pms+i-1] = pm_newmask (out, 1)
+ ncompress = 0
+ }
+ do i = 1, nimages {
+ pm = Memi[pms+i-1]
+ v[2] = line - l
+ if (v[2] > 0)
+ call pmplpi (pm, v, buf[1,i], 1, npts, rop)
+ if (l > 0) {
+ v[2] = line + l
+ if (v[2] <= nl)
+ call pmplpi (pm, v, buf[1,i], 1, npts, rop)
+ }
+ }
+ }
+ v[2] = line
+
+ if (ncompress > 10) {
+ do i = 1, nimages {
+ pm = Memi[pms+i-1]
+ call pm_compress (pm)
+ }
+ ncompress = 0
+ } else
+ ncompress = ncompress + 1
+end
+
+
+$for (sird)
+# IC_GROW$T -- Reject pixels.
+
+procedure ic_grow$t (v, d, m, n, buf, nimages, npts, pms)
+
+long v[ARB] # Output vector
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[ARB] # Number of good pixels
+int buf[ARB] # Buffer of npts
+int nimages # Number of images
+int npts # Number of output points per line
+pointer pms # Pointer to array of pixel masks
+
+int i, j, k
+pointer pm
+bool pl_linenotempty()
+
+include "../icombine.com"
+
+begin
+ do k = 1, nimages {
+ pm = Memi[pms+k-1]
+ if (!pl_linenotempty (pm, v))
+ next
+ call pmglpi (pm, v, buf, 1, npts, PIX_SRC)
+ do i = 1, npts {
+ if (buf[i] == 0)
+ next
+ for (j = 1; j <= n[i]; j = j + 1) {
+ if (Memi[m[j]+i-1] == k) {
+ if (j < n[i]) {
+ Mem$t[d[j]+i-1] = Mem$t[d[n[i]]+i-1]
+ Memi[m[j]+i-1] = Memi[m[n[i]]+i-1]
+ }
+ n[i] = n[i] - 1
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icgscale.x b/pkg/images/immatch/src/imcombine/src/icgscale.x
new file mode 100644
index 00000000..570697ad
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icgscale.x
@@ -0,0 +1,88 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "icombine.h"
+
+
+# IC_GSCALE -- Get scale values as directed by CL parameter.
+# Only those values which are INDEF are changed.
+# The values can be one of those in the dictionary, from a file specified
+# with a @ prefix, or from an image header keyword specified by a ! prefix.
+
+int procedure ic_gscale (param, name, dic, in, exptime, values, nimages)
+
+char param[ARB] #I CL parameter name
+char name[SZ_FNAME] #O Parameter value
+char dic[ARB] #I Dictionary string
+pointer in[nimages] #I IMIO pointers
+real exptime[nimages] #I Exposure times
+real values[nimages] #O Values
+int nimages #I Number of images
+
+int type #O Type of value
+
+int fd, i, nowhite(), open(), fscan(), nscan(), strdic()
+real rval, imgetr()
+pointer errstr
+errchk open, imgetr
+
+include "icombine.com"
+
+begin
+ call clgstr (param, name, SZ_FNAME)
+ if (nowhite (name, name, SZ_FNAME) == 0)
+ type = S_NONE
+ else if (name[1] == '@') {
+ type = S_FILE
+ do i = 1, nimages
+ if (IS_INDEFR(values[i]))
+ break
+ if (i <= nimages) {
+ fd = open (name[2], READ_ONLY, TEXT_FILE)
+ i = 0
+ while (fscan (fd) != EOF) {
+ call gargr (rval)
+ if (nscan() != 1)
+ next
+ if (i == nimages) {
+ call eprintf (
+ "Warning: Ignoring additional %s values in %s\n")
+ call pargstr (param)
+ call pargstr (name[2])
+ break
+ }
+ i = i + 1
+ if (IS_INDEFR(values[i]))
+ values[i] = rval
+ }
+ call close (fd)
+ if (i < nimages) {
+ call salloc (errstr, SZ_LINE, TY_CHAR)
+ call sprintf (errstr, SZ_FNAME,
+ "Insufficient %s values in %s")
+ call pargstr (param)
+ call pargstr (name[2])
+ call error (1, errstr)
+ }
+ }
+ } else if (name[1] == '!') {
+ type = S_KEYWORD
+ do i = 1, nimages {
+ if (IS_INDEFR(values[i]))
+ values[i] = imgetr (in[i], name[2])
+ if (project) {
+ call amovkr (values, values, nimages)
+ break
+ }
+ }
+ } else {
+ type = strdic (name, name, SZ_FNAME, dic)
+ if (type == 0)
+ call error (1, "Unknown scale, zero, or weight type")
+ if (type==S_EXPOSURE)
+ do i = 1, nimages
+ if (IS_INDEFR(values[i]))
+ values[i] = max (0.001, exptime[i])
+ }
+
+ return (type)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/ichdr.x b/pkg/images/immatch/src/imcombine/src/ichdr.x
new file mode 100644
index 00000000..b4d925c1
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/ichdr.x
@@ -0,0 +1,72 @@
+include <imset.h>
+
+
+# IC_HDR -- Set output header.
+
+procedure ic_hdr (in, out, nimages)
+
+pointer in[nimages] #I Input images
+pointer out[ARB] #I Output images
+int nimages #I Number of images
+
+int i, j, imgnfn(), nowhite(), strldxs()
+pointer sp, inkey, key, str, list, imofnlu()
+bool streq()
+
+begin
+ call smark (sp)
+ call salloc (inkey, SZ_FNAME, TY_CHAR)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ call clgstr ("imcmb", Memc[inkey], SZ_FNAME)
+ i = nowhite (Memc[inkey], Memc[inkey], SZ_FNAME)
+
+ if (i > 0 && streq (Memc[inkey], "$I")) {
+ # Set new PROCID.
+ call xt_procid (out)
+
+ # Set input PROCIDs.
+ if (nimages < 100) {
+ list = imofnlu (out, "PROCID[0-9][0-9]")
+ while (imgnfn (list, Memc[key], SZ_LINE) != EOF)
+ call imdelf (out, Memc[key])
+ call imcfnl (list)
+ do i = 1, nimages {
+ call sprintf (Memc[key], 8, "PROCID%02d")
+ call pargi (i)
+ iferr (call imgstr (in[i], "PROCID", Memc[str], SZ_LINE)) {
+ iferr (call imgstr (in[i], "OBSID", Memc[str], SZ_LINE))
+ Memc[str] = EOS
+ }
+ if (Memc[str] != EOS)
+ call imastr (out, Memc[key], Memc[str])
+ }
+ }
+ }
+
+ if (i > 0 && nimages < 1000) {
+ list = imofnlu (out, "IMCMB[0-9][0-9][0-9]")
+ while (imgnfn (list, Memc[key], SZ_LINE) != EOF)
+ call imdelf (out, Memc[key])
+ call imcfnl (list)
+ do i = 1, nimages {
+ if (streq (Memc[inkey], "$I")) {
+ call imstats (in[i], IM_IMAGENAME, Memc[str], SZ_LINE)
+ j = strldxs ("/$", Memc[str])
+ if (j > 0)
+ call strcpy (Memc[str+j], Memc[str], SZ_LINE)
+ } else {
+ iferr (call imgstr (in[i], Memc[inkey], Memc[str], SZ_LINE))
+ Memc[str] = EOS
+ }
+ if (Memc[str] == EOS)
+ next
+ call sprintf (Memc[key], SZ_LINE, "IMCMB%03d")
+ call pargi (i)
+ call imastr (out, Memc[key], Memc[str])
+ }
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icimstack.x b/pkg/images/immatch/src/imcombine/src/icimstack.x
new file mode 100644
index 00000000..d5628694
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icimstack.x
@@ -0,0 +1,186 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <imhdr.h>
+
+
+# IC_IMSTACK -- Stack images into a single image of higher dimension.
+
+procedure ic_imstack (list, output, mask)
+
+int list #I List of images
+char output[ARB] #I Name of output image
+char mask[ARB] #I Name of output mask
+
+int i, j, npix
+long line_in[IM_MAXDIM], line_out[IM_MAXDIM], line_outbpm[IM_MAXDIM]
+pointer sp, input, bpmname, key, in, out, inbpm, outbpm, buf_in, buf_out, ptr
+
+int imtgetim(), imtlen(), errget()
+int imgnls(), imgnli(), imgnll(), imgnlr(), imgnld(), imgnlx()
+int impnls(), impnli(), impnll(), impnlr(), impnld(), impnlx()
+pointer immap(), pm_newmask()
+errchk immap
+errchk imgnls, imgnli, imgnll, imgnlr, imgnld, imgnlx
+errchk impnls, impnli, impnll, impnlr, impnld, impnlx
+
+begin
+ call smark (sp)
+ call salloc (input, SZ_FNAME, TY_CHAR)
+ call salloc (bpmname, SZ_FNAME, TY_CHAR)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+
+ iferr {
+ # Add each input image to the output image.
+ out = NULL; outbpm = NULL
+ i = 0
+ while (imtgetim (list, Memc[input], SZ_FNAME) != EOF) {
+
+ i = i + 1
+ in = NULL; inbpm = NULL
+ ptr = immap (Memc[input], READ_ONLY, 0)
+ in = ptr
+
+ # For the first input image map the output image as a copy
+ # and increment the dimension. Set the output line counter.
+
+ if (i == 1) {
+ ptr = immap (output, NEW_COPY, in)
+ out = ptr
+ IM_NDIM(out) = IM_NDIM(out) + 1
+ IM_LEN(out, IM_NDIM(out)) = imtlen (list)
+ npix = IM_LEN(out, 1)
+ call amovkl (long(1), line_out, IM_MAXDIM)
+
+ if (mask[1] != EOS) {
+ ptr = immap (mask, NEW_COPY, in)
+ outbpm = ptr
+ IM_NDIM(outbpm) = IM_NDIM(outbpm) + 1
+ IM_LEN(outbpm, IM_NDIM(outbpm)) = imtlen (list)
+ call amovkl (long(1), line_outbpm, IM_MAXDIM)
+ }
+ }
+
+ # Check next input image for consistency with the output image.
+ if (IM_NDIM(in) != IM_NDIM(out) - 1)
+ call error (0, "Input images not consistent")
+ do j = 1, IM_NDIM(in) {
+ if (IM_LEN(in, j) != IM_LEN(out, j))
+ call error (0, "Input images not consistent")
+ }
+
+ call sprintf (Memc[key], SZ_FNAME, "stck%04d")
+ call pargi (i)
+ call imastr (out, Memc[key], Memc[input])
+
+ # Copy the input lines from the image to the next lines of
+ # the output image. Switch on the output data type to optimize
+ # IMIO.
+
+ call amovkl (long(1), line_in, IM_MAXDIM)
+ switch (IM_PIXTYPE (out)) {
+ case TY_SHORT:
+ while (imgnls (in, buf_in, line_in) != EOF) {
+ if (impnls (out, buf_out, line_out) == EOF)
+ call error (0, "Error writing output image")
+ call amovs (Mems[buf_in], Mems[buf_out], npix)
+ }
+ case TY_INT:
+ while (imgnli (in, buf_in, line_in) != EOF) {
+ if (impnli (out, buf_out, line_out) == EOF)
+ call error (0, "Error writing output image")
+ call amovi (Memi[buf_in], Memi[buf_out], npix)
+ }
+ case TY_USHORT, TY_LONG:
+ while (imgnll (in, buf_in, line_in) != EOF) {
+ if (impnll (out, buf_out, line_out) == EOF)
+ call error (0, "Error writing output image")
+ call amovl (Meml[buf_in], Meml[buf_out], npix)
+ }
+ case TY_REAL:
+ while (imgnlr (in, buf_in, line_in) != EOF) {
+ if (impnlr (out, buf_out, line_out) == EOF)
+ call error (0, "Error writing output image")
+ call amovr (Memr[buf_in], Memr[buf_out], npix)
+ }
+ case TY_DOUBLE:
+ while (imgnld (in, buf_in, line_in) != EOF) {
+ if (impnld (out, buf_out, line_out) == EOF)
+ call error (0, "Error writing output image")
+ call amovd (Memd[buf_in], Memd[buf_out], npix)
+ }
+ case TY_COMPLEX:
+ while (imgnlx (in, buf_in, line_in) != EOF) {
+ if (impnlx (out, buf_out, line_out) == EOF)
+ call error (0, "Error writing output image")
+ call amovx (Memx[buf_in], Memx[buf_out], npix)
+ }
+ default:
+ while (imgnlr (in, buf_in, line_in) != EOF) {
+ if (impnlr (out, buf_out, line_out) == EOF)
+ call error (0, "Error writing output image")
+ call amovr (Memr[buf_in], Memr[buf_out], npix)
+ }
+ }
+
+ # Copy mask.
+ if (mask[1] != EOS) {
+ iferr (call imgstr (in, "bpm", Memc[bpmname], SZ_FNAME)) {
+ Memc[bpmname] = EOS
+ ptr = pm_newmask (in, 27)
+ } else
+ ptr = immap (Memc[bpmname], READ_ONLY, 0)
+ inbpm = ptr
+
+ if (IM_NDIM(inbpm) != IM_NDIM(outbpm) - 1)
+ call error (0, "Input images not consistent")
+ do j = 1, IM_NDIM(inbpm) {
+ if (IM_LEN(inbpm, j) != IM_LEN(outbpm, j))
+ call error (0, "Masks not consistent")
+ }
+
+ call amovkl (long(1), line_in, IM_MAXDIM)
+ while (imgnli (inbpm, buf_in, line_in) != EOF) {
+ if (impnli (outbpm, buf_out, line_outbpm) == EOF)
+ call error (0, "Error writing output mask")
+ call amovi (Memi[buf_in], Memi[buf_out], npix)
+ }
+
+ call sprintf (Memc[key], SZ_FNAME, "bpm%04d")
+ call pargi (i)
+ call imastr (out, Memc[key], Memc[bpmname])
+
+ call imunmap (inbpm)
+ }
+
+ call imunmap (in)
+ }
+ } then {
+ i = errget (Memc[key], SZ_FNAME)
+ call erract (EA_WARN)
+ if (outbpm != NULL) {
+ call imunmap (outbpm)
+ iferr (call imdelete (mask))
+ ;
+ }
+ if (out != NULL) {
+ call imunmap (out)
+ iferr (call imdelete (output))
+ ;
+ }
+ if (inbpm != NULL)
+ call imunmap (inbpm)
+ if (in != NULL)
+ call imunmap (in)
+ call sfree (sp)
+ call error (i, "Can't make temporary stack images")
+ }
+
+ # Finish up.
+ if (outbpm != NULL) {
+ call imunmap (outbpm)
+ call imastr (out, "bpm", mask)
+ }
+ call imunmap (out)
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/iclog.x b/pkg/images/immatch/src/imcombine/src/iclog.x
new file mode 100644
index 00000000..53420cd5
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/iclog.x
@@ -0,0 +1,431 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include <mach.h>
+include "icombine.h"
+include "icmask.h"
+
+# IC_LOG -- Output log information is a log file has been specfied.
+
+procedure ic_log (in, out, ncombine, exptime, sname, zname, wname,
+ mode, median, mean, scales, zeros, wts, offsets, nimages,
+ dozero, nout)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+int ncombine[nimages] # Number of previous combined images
+real exptime[nimages] # Exposure times
+char sname[ARB] # Scale name
+char zname[ARB] # Zero name
+char wname[ARB] # Weight name
+real mode[nimages] # Modes
+real median[nimages] # Medians
+real mean[nimages] # Means
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero or sky levels
+real wts[nimages] # Weights
+int offsets[nimages,ARB] # Image offsets
+int nimages # Number of images
+bool dozero # Zero flag
+int nout # Number of images combined in output
+
+int i, j, stack, ctor()
+real rval, imgetr()
+long clktime()
+bool prncombine, prexptime, prmode, prmedian, prmean, prmask
+bool prrdn, prgain, prsn
+pointer sp, fname, bpname, key
+errchk imgetr
+
+include "icombine.com"
+
+begin
+ if (logfd == NULL)
+ return
+
+ call smark (sp)
+ call salloc (fname, SZ_LINE, TY_CHAR)
+ call salloc (bpname, SZ_LINE, TY_CHAR)
+
+ stack = NO
+ if (project) {
+ ifnoerr (call imgstr (in[1], "stck0001", Memc[fname], SZ_LINE))
+ stack = YES
+ }
+ if (stack == YES)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+
+ # Time stamp the log and print parameter information.
+
+ call cnvdate (clktime(0), Memc[fname], SZ_LINE)
+ call fprintf (logfd, "\n%s: IMCOMBINE\n")
+ call pargstr (Memc[fname])
+ switch (combine) {
+ case AVERAGE:
+ call fprintf (logfd, " combine = average, ")
+ case MEDIAN:
+ call fprintf (logfd, " combine = median, ")
+ case SUM:
+ call fprintf (logfd, " combine = sum, ")
+ }
+ call fprintf (logfd, "scale = %s, zero = %s, weight = %s\n")
+ call pargstr (sname)
+ call pargstr (zname)
+ call pargstr (wname)
+ if (combine == NMODEL && reject!=CCDCLIP && reject!=CRREJECT) {
+ call fprintf (logfd,
+ " rdnoise = %s, gain = %s, snoise = %s\n")
+ call pargstr (Memc[rdnoise])
+ call pargstr (Memc[gain])
+ call pargstr (Memc[snoise])
+ }
+
+ switch (reject) {
+ case MINMAX:
+ call fprintf (logfd, " reject = minmax, nlow = %d, nhigh = %d\n")
+ call pargi (nint (flow * nimages))
+ call pargi (nint (fhigh * nimages))
+ case CCDCLIP:
+ call fprintf (logfd, " reject = ccdclip, mclip = %b, nkeep = %d\n")
+ call pargb (mclip)
+ call pargi (nkeep)
+ call fprintf (logfd,
+ " rdnoise = %s, gain = %s, snoise = %s, sigma = %g, hsigma = %g\n")
+ call pargstr (Memc[rdnoise])
+ call pargstr (Memc[gain])
+ call pargstr (Memc[snoise])
+ call pargr (lsigma)
+ call pargr (hsigma)
+ case CRREJECT:
+ call fprintf (logfd,
+ " reject = crreject, mclip = %b, nkeep = %d\n")
+ call pargb (mclip)
+ call pargi (nkeep)
+ call fprintf (logfd,
+ " rdnoise = %s, gain = %s, snoise = %s, hsigma = %g\n")
+ call pargstr (Memc[rdnoise])
+ call pargstr (Memc[gain])
+ call pargstr (Memc[snoise])
+ call pargr (hsigma)
+ case PCLIP:
+ call fprintf (logfd, " reject = pclip, nkeep = %d\n")
+ call pargi (nkeep)
+ call fprintf (logfd, " pclip = %g, lsigma = %g, hsigma = %g\n")
+ call pargr (pclip)
+ call pargr (lsigma)
+ call pargr (hsigma)
+ case SIGCLIP:
+ call fprintf (logfd, " reject = sigclip, mclip = %b, nkeep = %d\n")
+ call pargb (mclip)
+ call pargi (nkeep)
+ call fprintf (logfd, " lsigma = %g, hsigma = %g\n")
+ call pargr (lsigma)
+ call pargr (hsigma)
+ case AVSIGCLIP:
+ call fprintf (logfd,
+ " reject = avsigclip, mclip = %b, nkeep = %d\n")
+ call pargb (mclip)
+ call pargi (nkeep)
+ call fprintf (logfd, " lsigma = %g, hsigma = %g\n")
+ call pargr (lsigma)
+ call pargr (hsigma)
+ }
+ if (reject != NONE && grow >= 1.) {
+ call fprintf (logfd, " grow = %g\n")
+ call pargr (grow)
+ }
+ if (dothresh) {
+ if (lthresh > -MAX_REAL && hthresh < MAX_REAL) {
+ call fprintf (logfd, " lthreshold = %g, hthreshold = %g\n")
+ call pargr (lthresh)
+ call pargr (hthresh)
+ } else if (lthresh > -MAX_REAL) {
+ call fprintf (logfd, " lthreshold = %g\n")
+ call pargr (lthresh)
+ } else {
+ call fprintf (logfd, " hthreshold = %g\n")
+ call pargr (hthresh)
+ }
+ }
+ call fprintf (logfd, " blank = %g\n")
+ call pargr (blank)
+ if (Memc[statsec] != EOS) {
+ call fprintf (logfd, " statsec = %s\n")
+ call pargstr (Memc[fname])
+ }
+
+ if (ICM_TYPE(icm) != M_NONE) {
+ switch (ICM_TYPE(icm)) {
+ case M_BOOLEAN, M_GOODVAL:
+ call fprintf (logfd, " masktype = goodval, maskval = %d\n")
+ call pargi (ICM_VALUE(icm))
+ case M_BADVAL:
+ call fprintf (logfd, " masktype = badval, maskval = %d\n")
+ call pargi (ICM_VALUE(icm))
+ case M_NOVAL:
+ call fprintf (logfd, " masktype = noval, maskval = %d\n")
+ call pargi (ICM_VALUE(icm))
+ case M_GOODBITS:
+ call fprintf (logfd, " masktype = goodbits, maskval = %d\n")
+ call pargi (ICM_VALUE(icm))
+ case M_BADBITS:
+ call fprintf (logfd, " masktype = badbits, maskval = %d\n")
+ call pargi (ICM_VALUE(icm))
+ case M_LTVAL:
+ call fprintf (logfd, " masktype = goodval, maskval < %d\n")
+ call pargi (ICM_VALUE(icm))
+ case M_GTVAL:
+ call fprintf (logfd, " masktype = goodval, maskval > %d\n")
+ call pargi (ICM_VALUE(icm))
+ }
+ }
+
+ # Print information pertaining to individual images as a set of
+ # columns with the image name being the first column. Determine
+ # what information is relevant and print the appropriate header.
+
+ prncombine = false
+ prexptime = false
+ prmode = false
+ prmedian = false
+ prmean = false
+ prmask = false
+ prrdn = false
+ prgain = false
+ prsn = false
+ do i = 1, nimages {
+ if (ncombine[i] != ncombine[1])
+ prncombine = true
+ if (exptime[i] != exptime[1])
+ prexptime = true
+ if (mode[i] != mode[1])
+ prmode = true
+ if (median[i] != median[1])
+ prmedian = true
+ if (mean[i] != mean[1])
+ prmean = true
+ if (ICM_TYPE(icm) != M_NONE) {
+ if (project)
+ bpname = Memi[ICM_NAMES(icm)]
+ else
+ bpname = Memi[ICM_NAMES(icm)+i-1]
+ if (Memc[bpname] != EOS)
+ prmask = true
+ }
+ if (combine == NMODEL || reject == CCDCLIP || reject == CRREJECT) {
+ j = 1
+ if (ctor (Memc[rdnoise], j, rval) == 0)
+ prrdn = true
+ j = 1
+ if (ctor (Memc[gain], j, rval) == 0)
+ prgain = true
+ j = 1
+ if (ctor (Memc[snoise], j, rval) == 0)
+ prsn = true
+ }
+ }
+
+ call fprintf (logfd, " %20s ")
+ call pargstr ("Images")
+ if (prncombine) {
+ call fprintf (logfd, " %6s")
+ call pargstr ("N")
+ }
+ if (prexptime) {
+ call fprintf (logfd, " %6s")
+ call pargstr ("Exp")
+ }
+ if (prmode) {
+ call fprintf (logfd, " %7s")
+ call pargstr ("Mode")
+ }
+ if (prmedian) {
+ call fprintf (logfd, " %7s")
+ call pargstr ("Median")
+ }
+ if (prmean) {
+ call fprintf (logfd, " %7s")
+ call pargstr ("Mean")
+ }
+ if (prrdn) {
+ call fprintf (logfd, " %7s")
+ call pargstr ("Rdnoise")
+ }
+ if (prgain) {
+ call fprintf (logfd, " %6s")
+ call pargstr ("Gain")
+ }
+ if (prsn) {
+ call fprintf (logfd, " %6s")
+ call pargstr ("Snoise")
+ }
+ if (doscale) {
+ call fprintf (logfd, " %6s")
+ call pargstr ("Scale")
+ }
+ if (dozero) {
+ call fprintf (logfd, " %7s")
+ call pargstr ("Zero")
+ }
+ if (dowts) {
+ call fprintf (logfd, " %6s")
+ call pargstr ("Weight")
+ }
+ if (!aligned) {
+ call fprintf (logfd, " %9s")
+ call pargstr ("Offsets")
+ }
+ if (prmask) {
+ call fprintf (logfd, " %s")
+ call pargstr ("Maskfile")
+ }
+ call fprintf (logfd, "\n")
+
+ do i = 1, nimages {
+ if (stack == YES) {
+ call sprintf (Memc[key], SZ_FNAME, "stck%04d")
+ call pargi (i)
+ ifnoerr (call imgstr (in[i], Memc[key], Memc[fname], SZ_LINE)) {
+ call fprintf (logfd, " %21s")
+ call pargstr (Memc[fname])
+ } else {
+ call imstats (in[i], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " %16s[%3d]")
+ call pargstr (Memc[fname])
+ call pargi (i)
+ }
+ } else if (project) {
+ call imstats (in[i], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " %16s[%3d]")
+ call pargstr (Memc[fname])
+ call pargi (i)
+ } else {
+ call imstats (in[i], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " %21s")
+ call pargstr (Memc[fname])
+ }
+ if (prncombine) {
+ call fprintf (logfd, " %6d")
+ call pargi (ncombine[i])
+ }
+ if (prexptime) {
+ call fprintf (logfd, " %6.1f")
+ call pargr (exptime[i])
+ }
+ if (prmode) {
+ call fprintf (logfd, " %7.5g")
+ call pargr (mode[i])
+ }
+ if (prmedian) {
+ call fprintf (logfd, " %7.5g")
+ call pargr (median[i])
+ }
+ if (prmean) {
+ call fprintf (logfd, " %7.5g")
+ call pargr (mean[i])
+ }
+ if (prrdn) {
+ rval = imgetr (in[i], Memc[rdnoise])
+ call fprintf (logfd, " %7g")
+ call pargr (rval)
+ }
+ if (prgain) {
+ rval = imgetr (in[i], Memc[gain])
+ call fprintf (logfd, " %6g")
+ call pargr (rval)
+ }
+ if (prsn) {
+ rval = imgetr (in[i], Memc[snoise])
+ call fprintf (logfd, " %6g")
+ call pargr (rval)
+ }
+ if (doscale) {
+ call fprintf (logfd, " %6.3f")
+ call pargr (1./scales[i])
+ }
+ if (dozero) {
+ call fprintf (logfd, " %7.5g")
+ call pargr (-zeros[i])
+ }
+ if (dowts) {
+ call fprintf (logfd, " %6.3f")
+ call pargr (wts[i])
+ }
+ if (!aligned) {
+ if (IM_NDIM(out[1]) == 1) {
+ call fprintf (logfd, " %9d")
+ call pargi (offsets[i,1])
+ } else {
+ do j = 1, IM_NDIM(out[1]) {
+ call fprintf (logfd, " %4d")
+ call pargi (offsets[i,j])
+ }
+ }
+ }
+ if (prmask) {
+ if (stack == YES) {
+ call sprintf (Memc[key], SZ_FNAME, "bpm%04d")
+ call pargi (i)
+ ifnoerr (call imgstr (in[i], Memc[key], Memc[fname],
+ SZ_LINE)) {
+ call fprintf (logfd, " %s")
+ call pargstr (Memc[fname])
+ } else {
+ call fprintf (logfd, " %s")
+ call pargstr (Memc[bpname])
+ }
+ } else if (ICM_TYPE(icm) != M_NONE) {
+ if (project)
+ bpname = Memi[ICM_NAMES(icm)]
+ else
+ bpname = Memi[ICM_NAMES(icm)+i-1]
+ if (Memc[bpname] != EOS) {
+ call fprintf (logfd, " %s")
+ call pargstr (Memc[bpname])
+ }
+ }
+ }
+ call fprintf (logfd, "\n")
+ }
+
+ # Log information about the output images.
+ call imstats (out[1], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, "\n Output image = %s, ncombine = %d")
+ call pargstr (Memc[fname])
+ call pargi (nout)
+ call fprintf (logfd, "\n")
+
+ if (out[2] != NULL) {
+ call imstats (out[2], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " Bad pixel mask = %s\n")
+ call pargstr (Memc[fname])
+ }
+
+ if (out[4] != NULL) {
+ call imstats (out[4], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " Rejection mask = %s\n")
+ call pargstr (Memc[fname])
+ }
+
+ if (out[5] != NULL) {
+ call imstats (out[5], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " Number rejected mask = %s\n")
+ call pargstr (Memc[fname])
+ }
+
+ if (out[6] != NULL) {
+ call imstats (out[6], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " Exposure mask = %s\n")
+ call pargstr (Memc[fname])
+ }
+
+ if (out[3] != NULL) {
+ call imstats (out[3], IM_IMAGENAME, Memc[fname], SZ_LINE)
+ call fprintf (logfd, " Sigma image = %s\n")
+ call pargstr (Memc[fname])
+ }
+
+ call flush (logfd)
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icmask.com b/pkg/images/immatch/src/imcombine/src/icmask.com
new file mode 100644
index 00000000..baba6f6a
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icmask.com
@@ -0,0 +1,8 @@
+# IMCMASK -- Common for IMCOMBINE mask interface.
+
+int mtype # Mask type
+int mvalue # Mask value
+pointer bufs # Pointer to data line buffers
+pointer pms # Pointer to array of PMIO pointers
+
+common /imcmask/ mtype, mvalue, bufs, pms
diff --git a/pkg/images/immatch/src/imcombine/src/icmask.h b/pkg/images/immatch/src/imcombine/src/icmask.h
new file mode 100644
index 00000000..ffb64aa9
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icmask.h
@@ -0,0 +1,12 @@
+# ICMASK -- Data structure for IMCOMBINE mask interface.
+
+define ICM_LEN 6 # Structure length
+define ICM_TYPE Memi[$1] # Mask type
+define ICM_VALUE Memi[$1+1] # Mask value
+define ICM_IOMODE Memi[$1+2] # I/O mode
+define ICM_BUFS Memi[$1+3] # Pointer to data line buffers
+define ICM_PMS Memi[$1+4] # Pointer to array of PMIO pointers
+define ICM_NAMES Memi[$1+5] # Pointer to array of mask names
+
+define ICM_OPEN 0 # Keep masks open
+define ICM_CLOSED 1 # Keep masks closed
diff --git a/pkg/images/immatch/src/imcombine/src/icmask.x b/pkg/images/immatch/src/imcombine/src/icmask.x
new file mode 100644
index 00000000..ca9c1d02
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icmask.x
@@ -0,0 +1,685 @@
+include <imhdr.h>
+include <imset.h>
+include <pmset.h>
+include "icombine.h"
+include "icmask.h"
+
+# IC_MASK -- ICOMBINE mask interface
+#
+# IC_MOPEN -- Initialize mask interface
+# IC_MCLOSE -- Close the mask interface
+# IC_MGET -- Get lines of mask pixels for all the images
+# IC_MGET1 -- Get a line of mask pixels for the specified image
+# IC_MCLOSE1-- Close a mask for the specified image index
+
+
+# IC_MOPEN -- Initialize mask interface.
+
+procedure ic_mopen (in, out, nimages, offsets, iomode)
+
+pointer in[nimages] #I Input images
+pointer out[ARB] #I Output images
+int nimages #I Number of images
+int offsets[nimages,ARB] #I Offsets to output image
+int iomode #I I/O mode
+
+int mtype # Mask type
+int mvalue # Mask value
+pointer bufs # Pointer to data line buffers
+pointer pms # Pointer to array of PMIO pointers
+pointer names # Pointer to array of string pointers
+
+int i, j, k, nin, nout, npix, npms, nscan(), strdic(), ctor()
+real rval
+pointer sp, str, key, fname, title, image, pm, pm_open()
+bool invert, pm_empty()
+errchk calloc, pm_open, ic_pmload
+
+include "icombine.com"
+
+begin
+ icm = NULL
+ if (IM_NDIM(out[1]) == 0)
+ return
+
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (title, SZ_FNAME, TY_CHAR)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+
+ # Determine the mask parameters and allocate memory.
+ # The mask buffers are initialize to all excluded so that
+ # output points outside the input data are always excluded
+ # and don't need to be set on a line-by-line basis.
+
+ mtype = M_NONE
+ call clgstr ("masktype", Memc[str], SZ_LINE)
+ call sscan (Memc[str])
+ call gargwrd (Memc[title], SZ_FNAME)
+ call gargwrd (Memc[key], SZ_FNAME)
+ i = nscan()
+ if (i > 0) {
+ if (Memc[title] == '!') {
+ if (i == 1)
+ mtype = M_GOODVAL
+ else
+ mtype = strdic (Memc[key], Memc[key], SZ_FNAME, MASKTYPES)
+ call strcpy (Memc[title+1], Memc[key], SZ_FNAME)
+ } else {
+ mtype = strdic (Memc[title], Memc[title], SZ_FNAME, MASKTYPES)
+ call strcpy ("BPM", Memc[key], SZ_FNAME)
+ }
+ if (mtype == 0) {
+ call sprintf (Memc[title], SZ_FNAME,
+ "Invalid or ambiguous masktype (%s)")
+ call pargstr (Memc[str])
+ call error (1, Memc[title])
+ }
+ }
+ npix = IM_LEN(out[1],1)
+ call calloc (pms, nimages, TY_POINTER)
+ call calloc (bufs, nimages, TY_POINTER)
+ call calloc (names, nimages, TY_POINTER)
+ do i = 1, nimages {
+ call malloc (Memi[bufs+i-1], npix, TY_INT)
+ call amovki (1, Memi[Memi[bufs+i-1]], npix)
+ }
+
+ # Check for special cases. The BOOLEAN type is used when only
+ # zero and nonzero are significant; i.e. the actual mask values are
+ # not important. The invert flag is used to indicate that
+ # empty masks are all bad rather the all good.
+
+ # Eventually we want to allow general expressions. For now we only
+ # allow a special '<' or '>' operator.
+
+ call clgstr ("maskvalue", Memc[title], SZ_FNAME)
+ i = 1
+ if (Memc[title] == '<') {
+ mtype = M_LTVAL
+ i = i + 1
+ } else if (Memc[title] == '>') {
+ mtype = M_GTVAL
+ i = i + 1
+ }
+ if (ctor (Memc[title], i, rval) == 0)
+ call error (1, "Bad mask value")
+ mvalue = rval
+ if (mvalue < 0)
+ call error (1, "Bad mask value")
+ else if (mvalue == 0 && mtype == M_NOVAL)
+ call error (1, "maskvalue cannot be 0 for masktype of 'novalue'")
+
+ if (mtype == 0)
+ mtype = M_NONE
+ else if (mtype == M_BADBITS && mvalue == 0)
+ mtype = M_NONE
+ else if (mvalue == 0 && (mtype == M_GOODVAL || mtype == M_GOODBITS))
+ mtype = M_BOOLEAN
+ else if ((mtype == M_BADVAL && mvalue == 0) ||
+ (mtype == M_GOODVAL && mvalue != 0) ||
+ (mtype == M_GOODBITS && mvalue == 0))
+ invert = true
+ else
+ invert = false
+
+ # If mask images are to be used, get the mask name from the image
+ # header and open it saving the descriptor in the pms array.
+ # Empty masks (all good) are treated as if there was no mask image.
+
+ nout = IM_LEN(out[1],1)
+ npms = 0
+ do i = 1, nimages {
+ if (mtype != M_NONE) {
+ call malloc (Memi[names+i-1], SZ_FNAME, TY_CHAR)
+ fname = Memi[names+i-1]
+ ifnoerr (call imgstr (in[i],Memc[key],Memc[fname],SZ_FNAME)) {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+ if (npix < 1)
+ Memc[fname] = EOS
+ else {
+ pm = pm_open (NULL)
+ call ic_pmload (in[i], pm, Memc[fname], SZ_FNAME)
+ call pm_seti (pm, P_REFIM, in[i])
+ if (pm_empty (pm) && !invert)
+ Memc[fname] = EOS
+ else {
+ if (project)
+ npms = nimages
+ else
+ npms = npms + 1
+ }
+ call pm_close (pm)
+ }
+ if (project)
+ break
+ } else
+ Memc[fname] = EOS
+ }
+ }
+
+ # If no mask images are found and the mask parameters imply that
+ # good values are 0 then use the special case of no masks.
+
+ if (npms == 0) {
+ if (!invert)
+ mtype = M_NONE
+ }
+
+ # Set up mask structure.
+ call calloc (icm, ICM_LEN, TY_STRUCT)
+ ICM_TYPE(icm) = mtype
+ ICM_VALUE(icm) = mvalue
+ ICM_IOMODE(icm) = iomode
+ ICM_BUFS(icm) = bufs
+ ICM_PMS(icm) = pms
+ ICM_NAMES(icm) = names
+
+ call sfree (sp)
+end
+
+
+# IC_PMLOAD -- Find and load a mask.
+# This is more complicated because we want to allow a mask name specified
+# without a path to be found either in the current directory or in the
+# directory of the image.
+
+procedure ic_pmload (im, pm, fname, maxchar)
+
+pointer im #I Image pointer to be associated with mask
+pointer pm #O Mask pointer to be returned
+char fname[ARB] #U Mask name
+int maxchar #I Max size of mask name
+
+bool match
+pointer sp, str, imname, yt_pmload()
+int i, fnldir(), stridxs(), envfind()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_PATHNAME, TY_CHAR)
+
+ # First check if the specified file can be loaded.
+ match = (envfind ("pmatch", Memc[str], SZ_PATHNAME) > 0)
+ if (match) {
+ call pm_close (pm)
+ iferr (pm = yt_pmload (fname,im,"logical",Memc[str],SZ_PATHNAME))
+ pm = NULL
+ if (pm != NULL)
+ return
+ } else {
+ ifnoerr (call pm_loadf (pm, fname, Memc[str], SZ_PATHNAME))
+ return
+ ifnoerr (call pm_loadim (pm, fname, Memc[str], SZ_PATHNAME))
+ return
+ }
+
+ # Check if the file has a path in which case we return an error.
+ # Must deal with possible [] which is a VMS directory delimiter.
+ call strcpy (fname, Memc[str], SZ_PATHNAME)
+ i = stridxs ("[", Memc[str])
+ if (i > 0)
+ Memc[str+i-1] = EOS
+ if (fnldir (Memc[str], Memc[str], SZ_PATHNAME) > 0) {
+ call sprintf (Memc[str], SZ_PATHNAME,
+ "Bad pixel mask not found (%s)")
+ call pargstr (fname)
+ call error (1, Memc[str])
+ }
+
+ # Check if the image has a path. If not return an error.
+ call salloc (imname, SZ_PATHNAME, TY_CHAR)
+ call imstats (im, IM_IMAGENAME, Memc[imname], SZ_PATHNAME)
+ if (fnldir (Memc[imname], Memc[str], SZ_PATHNAME) == 0) {
+ call sprintf (Memc[str], SZ_PATHNAME,
+ "Bad pixel mask not found (%s)")
+ call pargstr (fname)
+ call error (1, Memc[str])
+ }
+
+ # Try using the image path for the mask file.
+ call strcat (fname, Memc[str], SZ_PATHNAME)
+ if (match) {
+ iferr (pm = yt_pmload (Memc[imname], im, "logical",
+ Memc[str], SZ_PATHNAME))
+ pm = NULL
+ if (pm != NULL) {
+ call strcpy (Memc[str], fname, maxchar)
+ return
+ }
+ } else {
+ ifnoerr (call pm_loadf (pm, Memc[str], Memc[imname], SZ_PATHNAME)) {
+ call strcpy (Memc[str], fname, maxchar)
+ return
+ }
+ }
+
+ # No mask found.
+ call sprintf (Memc[str], SZ_PATHNAME,
+ "Bad pixel mask not found (%s)")
+ call pargstr (fname)
+ call error (1, Memc[str])
+
+ # This will not be reached and we let the calling program free
+ # the stack. We include smark/sfree for lint detectors.
+ call sfree (sp)
+end
+
+
+
+# IC_MCLOSE -- Close the mask interface.
+
+procedure ic_mclose (nimages)
+
+int nimages # Number of images
+
+int i
+include "icombine.com"
+
+begin
+ if (icm == NULL)
+ return
+
+ do i = 1, nimages {
+ call mfree (Memi[ICM_NAMES(icm)+i-1], TY_CHAR)
+ call mfree (Memi[ICM_BUFS(icm)+i-1], TY_INT)
+ }
+ do i = 1, nimages {
+ if (Memi[ICM_PMS(icm)+i-1] != NULL)
+ call pm_close (Memi[ICM_PMS(icm)+i-1])
+ if (project)
+ break
+ }
+ call mfree (ICM_NAMES(icm), TY_POINTER)
+ call mfree (ICM_BUFS(icm), TY_POINTER)
+ call mfree (ICM_PMS(icm), TY_POINTER)
+ call mfree (icm, TY_STRUCT)
+end
+
+
+# IC_MGET -- Get lines of mask pixels in the output coordinate system.
+# This converts the mask format to an array where zero is good and nonzero
+# is bad. This has special cases for optimization.
+
+procedure ic_mget (in, out, offsets, v1, v2, m, lflag, nimages, mtype)
+
+pointer in[nimages] # Input image pointers
+pointer out[ARB] # Output image pointer
+int offsets[nimages,ARB] # Offsets to output image
+long v1[IM_MAXDIM] # Data vector desired in output image
+long v2[IM_MAXDIM] # Data vector in input image
+pointer m[nimages] # Pointer to mask pointers
+int lflag[nimages] # Line flags
+int nimages # Number of images
+
+int mtype # Mask type
+int mvalue # Mask value
+int iomode # I/O mode
+pointer bufs # Pointer to data line buffers
+pointer pms # Pointer to array of PMIO pointers
+
+char title[1]
+int i, j, k, l, ndim, nin, nout, npix, envfind()
+pointer buf, pm, names, fname, pm_open(), yt_pmload()
+bool match, pm_linenotempty()
+errchk pm_glpi, pm_open, pm_loadf, pm_loadim, yt_pmload
+
+include "icombine.com"
+
+begin
+ # Determine if masks are needed at all. Note that the threshold
+ # is applied by simulating mask values so the mask pointers have to
+ # be set.
+
+ dflag = D_ALL
+ mtype = M_NONE
+ if (icm == NULL)
+ return
+ if (ICM_TYPE(icm) == M_NONE && aligned && !dothresh)
+ return
+
+ mtype = ICM_TYPE(icm)
+ mvalue = ICM_VALUE(icm)
+ iomode = ICM_IOMODE(icm)
+ bufs = ICM_BUFS(icm)
+ pms = ICM_PMS(icm)
+ names = ICM_NAMES(icm)
+ match = (envfind ("pmmatch", title, 1) > 0)
+
+ # Set the mask pointers and line flags and apply offsets if needed.
+
+ ndim = IM_NDIM(out[1])
+ nout = IM_LEN(out[1],1)
+ do i = 1, nimages {
+ nin = IM_LEN(in[i],1)
+ j = max (0, offsets[i,1])
+ k = min (nout, nin + offsets[i,1])
+ npix = k - j
+
+ m[i] = Memi[bufs+i-1]
+ buf = Memi[bufs+i-1] + j
+ if (project) {
+ pm = Memi[pms]
+ fname = Memi[names]
+ } else {
+ pm = Memi[pms+i-1]
+ fname = Memi[names+i-1]
+ }
+
+ if (npix < 1)
+ lflag[i] = D_NONE
+ else if (npix == nout)
+ lflag[i] = D_ALL
+ else
+ lflag[i] = D_MIX
+
+ if (lflag[i] != D_NONE) {
+ v2[1] = 1 + j - offsets[i,1]
+ do l = 2, ndim {
+ v2[l] = v1[l] - offsets[i,l]
+ if (v2[l] < 1 || v2[l] > IM_LEN(in[i],l)) {
+ lflag[i] = D_NONE
+ break
+ }
+ }
+ }
+ if (project)
+ v2[ndim+1] = i
+
+ if (lflag[i] == D_NONE) {
+ if (pm != NULL && !project) {
+ call pm_close (pm)
+ Memi[pms+i-1] = NULL
+ }
+ call amovki (1, Memi[m[i]], nout)
+ next
+ } else if (lflag[i] == D_MIX) {
+ if (j > 0)
+ call amovki (1, Memi[m[i]], j)
+ if (nout-k > 0)
+ call amovki (1, Memi[m[i]+k], nout-k)
+ }
+
+ if (fname == NULL) {
+ call aclri (Memi[buf], npix)
+ next
+ } else if (Memc[fname] == EOS) {
+ call aclri (Memi[buf], npix)
+ next
+ }
+
+ # Do mask I/O and convert to appropriate values in order of
+ # expected usage.
+
+ if (pm == NULL) {
+ if (match) {
+ pm = yt_pmload (Memc[fname], in[i], "logical",
+ Memc[fname], SZ_FNAME)
+ } else {
+ pm = pm_open (NULL)
+ iferr (call pm_loadf (pm, Memc[fname], title, 1))
+ call pm_loadim (pm, Memc[fname], title, 1)
+ call pm_seti (pm, P_REFIM, in[i])
+ }
+ if (project)
+ Memi[pms] = pm
+ else
+ Memi[pms+i-1] = pm
+ }
+
+ if (pm_linenotempty (pm, v2)) {
+ call pm_glpi (pm, v2, Memi[buf], 32, npix, 0)
+
+ if (mtype == M_BOOLEAN)
+ ;
+ else if (mtype == M_BADBITS)
+ call aandki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_BADVAL)
+ call abeqki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_NOVAL) {
+ do j = 0, npix-1 {
+ if (Memi[buf+j] == 0)
+ next
+ if (Memi[buf+j] == mvalue)
+ Memi[buf+j] = 1
+ else
+ Memi[buf+j] = 2
+ }
+ } else if (mtype == M_GOODBITS) {
+ call aandki (Memi[buf], mvalue, Memi[buf], npix)
+ call abeqki (Memi[buf], 0, Memi[buf], npix)
+ } else if (mtype == M_GOODVAL)
+ call abneki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_LTVAL)
+ call abgeki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_GTVAL)
+ call ableki (Memi[buf], mvalue, Memi[buf], npix)
+
+ lflag[i] = D_NONE
+ do j = 1, npix
+ if (Memi[buf+j-1] != 1) {
+ lflag[i] = D_MIX
+ break
+ }
+ } else {
+ if (mtype == M_BOOLEAN || mtype == M_BADBITS) {
+ call aclri (Memi[buf], npix)
+ } else if ((mtype == M_BADVAL && mvalue != 0) ||
+ (mtype == M_NOVAL && mvalue != 0) ||
+ (mtype == M_GOODVAL && mvalue == 0)) {
+ call aclri (Memi[buf], npix)
+ } else if (mtype == M_LTVAL && mvalue > 0) {
+ call aclri (Memi[buf], npix)
+ } else {
+ call amovki (1, Memi[buf], npix)
+ lflag[i] = D_NONE
+ }
+ }
+
+ if (iomode == ICM_CLOSED)
+ call ic_mclose1 (i, nimages)
+ }
+
+ # Set overall data flag
+ dflag = lflag[1]
+ do i = 2, nimages {
+ if (lflag[i] != dflag) {
+ dflag = D_MIX
+ break
+ }
+ }
+end
+
+
+# IC_MGET1 -- Get line of mask pixels from a specified image.
+# This is used by the IC_STAT procedure. This procedure converts the
+# stored mask format to an array where zero is good and nonzero is bad.
+# The data vector and returned mask array are in the input image pixel system.
+
+procedure ic_mget1 (in, image, nimages, offset, v, m)
+
+pointer in # Input image pointer
+int image # Image index
+int nimages # Number of images
+int offset # Column offset
+long v[IM_MAXDIM] # Data vector desired
+pointer m # Pointer to mask
+
+int mtype # Mask type
+int mvalue # Mask value
+pointer bufs # Pointer to data line buffers
+pointer pms # Pointer to array of PMIO pointers
+
+char title[1]
+int i, npix, envfind()
+pointer buf, pm, names, fname, pm_open(), yt_pmload()
+bool pm_linenotempty()
+errchk pm_glpi, pm_open, pm_loadf, pm_loadim, yt_pmload
+
+include "icombine.com"
+
+begin
+ dflag = D_ALL
+ if (icm == NULL)
+ return
+ if (ICM_TYPE(icm) == M_NONE)
+ return
+
+ mtype = ICM_TYPE(icm)
+ mvalue = ICM_VALUE(icm)
+ bufs = ICM_BUFS(icm)
+ pms = ICM_PMS(icm)
+ names = ICM_NAMES(icm)
+
+ npix = IM_LEN(in,1)
+ m = Memi[bufs+image-1] + offset
+ if (project) {
+ pm = Memi[pms]
+ fname = Memi[names]
+ } else {
+ pm = Memi[pms+image-1]
+ fname = Memi[names+image-1]
+ }
+
+ if (fname == NULL)
+ return
+ if (Memc[fname] == EOS)
+ return
+
+ if (pm == NULL) {
+ if (envfind ("pmmatch", title, 1) > 0) {
+ pm = yt_pmload (Memc[fname], in, "logical", Memc[fname],
+ SZ_FNAME)
+ } else {
+ pm = pm_open (NULL)
+ iferr (call pm_loadf (pm, Memc[fname], title, 1))
+ call pm_loadim (pm, Memc[fname], title, 1)
+ call pm_seti (pm, P_REFIM, in)
+ }
+ if (project)
+ Memi[pms] = pm
+ else
+ Memi[pms+image-1] = pm
+ }
+
+ # Do mask I/O and convert to appropriate values in order of
+ # expected usage.
+
+ buf = m
+ if (pm_linenotempty (pm, v)) {
+ call pm_glpi (pm, v, Memi[buf], 32, npix, 0)
+
+ if (mtype == M_BOOLEAN)
+ ;
+ else if (mtype == M_BADBITS)
+ call aandki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_BADVAL)
+ call abeqki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_NOVAL) {
+ do i = 0, npix-1 {
+ if (Memi[buf+i] == 0)
+ next
+ if (Memi[buf+i] == mvalue)
+ Memi[buf+i] = 1
+ else
+ Memi[buf+i] = 2
+ }
+ } else if (mtype == M_GOODBITS) {
+ call aandki (Memi[buf], mvalue, Memi[buf], npix)
+ call abeqki (Memi[buf], 0, Memi[buf], npix)
+ } else if (mtype == M_GOODVAL)
+ call abneki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_LTVAL)
+ call abgeki (Memi[buf], mvalue, Memi[buf], npix)
+ else if (mtype == M_GTVAL)
+ call ableki (Memi[buf], mvalue, Memi[buf], npix)
+
+ dflag = D_NONE
+ do i = 1, npix
+ if (Memi[buf+i-1] != 1) {
+ dflag = D_MIX
+ break
+ }
+ } else {
+ if (mtype == M_BOOLEAN || mtype == M_BADBITS) {
+ ;
+ } else if ((mtype == M_BADVAL && mvalue != 0) ||
+ (mtype == M_NOVAL && mvalue != 0) ||
+ (mtype == M_GOODVAL && mvalue == 0)) {
+ ;
+ } else if (mtype == M_LTVAL && mvalue > 0) {
+ ;
+ } else
+ dflag = D_NONE
+ }
+end
+
+
+# IC_MCLOSE1 -- Close mask by index.
+
+procedure ic_mclose1 (image, nimages)
+
+int image # Image index
+int nimages # Number of images
+
+pointer pms, names, pm, fname
+include "icombine.com"
+
+begin
+ if (icm == NULL)
+ return
+
+ pms = ICM_PMS(icm)
+ names = ICM_NAMES(icm)
+
+ if (project) {
+ pm = Memi[pms]
+ fname = Memi[names]
+ } else {
+ pm = Memi[pms+image-1]
+ fname = Memi[names+image-1]
+ }
+
+ if (fname == NULL || pm == NULL)
+ return
+ if (Memc[fname] == EOS || pm == NULL)
+ return
+
+ call pm_close (pm)
+ if (project)
+ Memi[pms] = NULL
+ else
+ Memi[pms+image-1] = NULL
+end
+
+
+# YT_PMLOAD -- This is like yt_mappm except it returns the mask pointer.
+
+pointer procedure yt_pmload (pmname, refim, match, mname, sz_mname)
+
+char pmname[ARB] #I Pixel mask name
+pointer refim #I Reference image pointer
+char match[ARB] #I Match by physical coordinates?
+char mname[ARB] #O Expanded mask name
+int sz_mname #O Size of expanded mask name
+pointer pm #R Pixel mask pointer
+
+int imstati()
+pointer im, yt_mappm()
+errchk yt_mappm
+
+begin
+ im = yt_mappm (pmname, refim, match, mname, sz_mname)
+ if (im != NULL) {
+ pm = imstati (im, IM_PMDES)
+ call imseti (im, IM_PMDES, NULL)
+ call imunmap (im)
+ } else
+ pm = NULL
+ return (pm)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icmedian.gx b/pkg/images/immatch/src/imcombine/src/icmedian.gx
new file mode 100644
index 00000000..164140a1
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icmedian.gx
@@ -0,0 +1,246 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+$for (sird)
+# IC_MEDIAN -- Median of lines
+
+procedure ic_median$t (d, n, npts, doblank, median)
+
+pointer d[ARB] # Input data line pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+int doblank # Set blank values?
+$if (datatype == sil)
+real median[npts] # Median
+$else
+PIXEL median[npts] # Median
+$endif
+
+int i, j, k, j1, j2, n1, lo, up, lo1, up1
+bool even
+$if (datatype == silx)
+real val1, val2, val3
+$else
+PIXEL val1, val2, val3
+$endif
+PIXEL temp, wtemp
+$if (datatype == x)
+real abs_temp
+$endif
+
+include "../icombine.com"
+
+begin
+ # If no data return after possibly setting blank values.
+ if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ median[i]= blank
+ }
+ return
+ }
+
+ # If the data were previously sorted then directly compute the median.
+ if (mclip) {
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ j1 = n1 / 2 + 1
+ j2 = n1 / 2
+ even = (mod(n1,2)==0 && (medtype==MEDAVG || n1>2))
+ do i = 1, npts {
+ k = i - 1
+ if (even) {
+ val1 = Mem$t[d[j1]+k]
+ val2 = Mem$t[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Mem$t[d[j1]+k]
+ }
+ return
+ } else {
+ # Check for negative n values. If found then there are
+ # pixels with no good values but with values we want to
+ # use as a substitute median. In this case ignore that
+ # the good pixels have been sorted.
+ do i = 1, npts {
+ if (n[i] < 0)
+ break
+ }
+
+ if (n[i] >= 0) {
+ do i = 1, npts {
+ k = i - 1
+ n1 = n[i]
+ if (n1 > 0) {
+ j1 = n1 / 2 + 1
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1>2)) {
+ j2 = n1 / 2
+ val1 = Mem$t[d[j1]+k]
+ val2 = Mem$t[d[j2]+k]
+ median[i] = (val1 + val2) / 2.
+ } else
+ median[i] = Mem$t[d[j1]+k]
+ } else if (doblank == YES)
+ median[i] = blank
+ }
+ return
+ }
+ }
+ }
+
+ # Compute the median.
+ do i = 1, npts {
+ k = i - 1
+ n1 = abs(n[i])
+
+ # If there are more than 3 points use Wirth algorithm. This
+ # is the same as vops$amed.gx except for an even number of
+ # points it selects the middle two and averages.
+ if (n1 > 3) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2))
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Mem$t[d[j]+k]; lo1 = lo; up1 = up
+ $if (datatype == x)
+ abs_temp = abs (temp)
+ $endif
+
+ repeat {
+ $if (datatype == x)
+ while (abs (Mem$t[d[lo1]+k]) < abs_temp)
+ $else
+ while (Mem$t[d[lo1]+k] < temp)
+ $endif
+ lo1 = lo1 + 1
+ $if (datatype == x)
+ while (abs_temp < abs (Mem$t[d[up1]+k]))
+ $else
+ while (temp < Mem$t[d[up1]+k])
+ $endif
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Mem$t[d[lo1]+k]
+ Mem$t[d[lo1]+k] = Mem$t[d[up1]+k]
+ Mem$t[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+
+ median[i] = Mem$t[d[j]+k]
+
+ if (mod(n1,2)==0 && (medtype==MEDAVG || n1 > 2)) {
+ lo = 1
+ up = n1
+ j = max (lo, min (up, (up+1)/2)+1)
+
+ while (lo < up) {
+ if (! (lo < up))
+ break
+
+ temp = Mem$t[d[j]+k]; lo1 = lo; up1 = up
+ $if (datatype == x)
+ abs_temp = abs (temp)
+ $endif
+
+ repeat {
+ $if (datatype == x)
+ while (abs (Mem$t[d[lo1]+k]) < abs_temp)
+ $else
+ while (Mem$t[d[lo1]+k] < temp)
+ $endif
+ lo1 = lo1 + 1
+ $if (datatype == x)
+ while (abs_temp < abs (Mem$t[d[up1]+k]))
+ $else
+ while (temp < Mem$t[d[up1]+k])
+ $endif
+ up1 = up1 - 1
+ if (lo1 <= up1) {
+ wtemp = Mem$t[d[lo1]+k]
+ Mem$t[d[lo1]+k] = Mem$t[d[up1]+k]
+ Mem$t[d[up1]+k] = wtemp
+ lo1 = lo1 + 1; up1 = up1 - 1
+ }
+ } until (lo1 > up1)
+
+ if (up1 < j)
+ lo = lo1
+ if (j < lo1)
+ up = up1
+ }
+ median[i] = (median[i] + Mem$t[d[j]+k]) / 2
+ }
+
+ # If 3 points find the median directly.
+ } else if (n1 == 3) {
+ $if (datatype == x)
+ val1 = abs (Mem$t[d[1]+k])
+ val2 = abs (Mem$t[d[2]+k])
+ val3 = abs (Mem$t[d[3]+k])
+ if (val1 < val2) {
+ if (val2 < val3) # abc
+ median[i] = Mem$t[d[2]+k]
+ else if (val1 < val3) # acb
+ median[i] = Mem$t[d[3]+k]
+ else # cab
+ median[i] = Mem$t[d[1]+k]
+ } else {
+ if (val2 > val3) # cba
+ median[i] = Mem$t[d[2]+k]
+ else if (val1 > val3) # bca
+ median[i] = Mem$t[d[3]+k]
+ else # bac
+ median[i] = Mem$t[d[1]+k]
+ }
+ $else
+ val1 = Mem$t[d[1]+k]
+ val2 = Mem$t[d[2]+k]
+ val3 = Mem$t[d[3]+k]
+ if (val1 < val2) {
+ if (val2 < val3) # abc
+ median[i] = val2
+ else if (val1 < val3) # acb
+ median[i] = val3
+ else # cab
+ median[i] = val1
+ } else {
+ if (val2 > val3) # cba
+ median[i] = val2
+ else if (val1 > val3) # bca
+ median[i] = val3
+ else # bac
+ median[i] = val1
+ }
+ $endif
+
+ # If 2 points average.
+ } else if (n1 == 2) {
+ val1 = Mem$t[d[1]+k]
+ val2 = Mem$t[d[2]+k]
+ if (medtype == MEDAVG)
+ median[i] = (val1 + val2) / 2
+ else
+ median[i] = min (val1, val2)
+
+ # If 1 point return the value.
+ } else if (n1 == 1)
+ median[i] = Mem$t[d[1]+k]
+
+ # If no points return with a possibly blank value.
+ else if (doblank == YES)
+ median[i] = blank
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icmm.gx b/pkg/images/immatch/src/imcombine/src/icmm.gx
new file mode 100644
index 00000000..860cb512
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icmm.gx
@@ -0,0 +1,189 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+$for (sird)
+# IC_MM -- Reject a specified number of high and low pixels
+
+procedure ic_mm$t (d, m, n, npts)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of good pixels
+int npts # Number of output points per line
+
+int n1, ncombine, npairs, nlow, nhigh, np
+int i, i1, j, jmax, jmin
+pointer k, kmax, kmin
+PIXEL d1, d2, dmin, dmax
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_NONE)
+ return
+
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = n1 - nlow - nhigh
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ do i = 1, npts {
+ i1 = i - 1
+ n1 = max (0, n[i])
+ if (dflag == D_MIX) {
+ nlow = flow * n1 + 0.001
+ nhigh = fhigh * n1 + 0.001
+ ncombine = max (ncombine, n1 - nlow - nhigh)
+ npairs = min (nlow, nhigh)
+ nlow = nlow - npairs
+ nhigh = nhigh - npairs
+ }
+
+ # Reject the npairs low and high points.
+ do np = 1, npairs {
+ k = d[1] + i1
+ $if (datatype == x)
+ d1 = abs (Mem$t[k])
+ $else
+ d1 = Mem$t[k]
+ $endif
+ dmax = d1; dmin = d1; jmax = 1; jmin = 1; kmax = k; kmin = k
+ do j = 2, n1 {
+ d2 = d1
+ k = d[j] + i1
+ $if (datatype == x)
+ d1 = abs (Mem$t[k])
+ $else
+ d1 = Mem$t[k]
+ $endif
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ } else if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ j = n1 - 1
+ if (keepids) {
+ if (jmax < j) {
+ if (jmin != j) {
+ Mem$t[kmax] = d2
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ } else {
+ Mem$t[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ }
+ if (jmin < j) {
+ if (jmax != n1) {
+ Mem$t[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ } else {
+ Mem$t[kmin] = d2
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[j]+i1]
+ Memi[m[j]+i1] = k
+ }
+ }
+ } else {
+ if (jmax < j) {
+ if (jmin != j)
+ Mem$t[kmax] = d2
+ else
+ Mem$t[kmax] = d1
+ }
+ if (jmin < j) {
+ if (jmax != n1)
+ Mem$t[kmin] = d1
+ else
+ Mem$t[kmin] = d2
+ }
+ }
+ n1 = n1 - 2
+ }
+
+ # Reject the excess low points.
+ do np = 1, nlow {
+ k = d[1] + i1
+ $if (datatype == x)
+ d1 = abs (Mem$t[k])
+ $else
+ d1 = Mem$t[k]
+ $endif
+ dmin = d1; jmin = 1; kmin = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ $if (datatype == x)
+ d1 = abs (Mem$t[k])
+ $else
+ d1 = Mem$t[k]
+ $endif
+ if (d1 < dmin) {
+ dmin = d1; jmin = j; kmin = k
+ }
+ }
+ if (keepids) {
+ if (jmin < n1) {
+ Mem$t[kmin] = d1
+ k = Memi[m[jmin]+i1]
+ Memi[m[jmin]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmin < n1)
+ Mem$t[kmin] = d1
+ }
+ n1 = n1 - 1
+ }
+
+ # Reject the excess high points.
+ do np = 1, nhigh {
+ k = d[1] + i1
+ $if (datatype == x)
+ d1 = abs (Mem$t[k])
+ $else
+ d1 = Mem$t[k]
+ $endif
+ dmax = d1; jmax = 1; kmax = k
+ do j = 2, n1 {
+ k = d[j] + i1
+ $if (datatype == x)
+ d1 = abs (Mem$t[k])
+ $else
+ d1 = Mem$t[k]
+ $endif
+ if (d1 > dmax) {
+ dmax = d1; jmax = j; kmax = k
+ }
+ }
+ if (keepids) {
+ if (jmax < n1) {
+ Mem$t[kmax] = d1
+ k = Memi[m[jmax]+i1]
+ Memi[m[jmax]+i1] = Memi[m[n1]+i1]
+ Memi[m[n1]+i1] = k
+ }
+ } else {
+ if (jmax < n1)
+ Mem$t[kmax] = d1
+ }
+ n1 = n1 - 1
+ }
+ n[i] = n1
+ }
+
+ if (dflag == D_ALL && npairs + nlow + nhigh > 0)
+ dflag = D_MIX
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icnmodel.gx b/pkg/images/immatch/src/imcombine/src/icnmodel.gx
new file mode 100644
index 00000000..0e020dc9
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icnmodel.gx
@@ -0,0 +1,147 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+include "../icmask.h"
+
+$for (sird)
+# IC_NMODEL -- Compute the quadrature average (or summed) noise model.
+# Options include a weighted average/sum.
+
+procedure ic_nmodel$t (d, m, n, nm, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real nm[3,nimages] # Noise model parameters
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+$if (datatype == sil)
+real average[npts] # Average (returned)
+$else
+PIXEL average[npts] # Average (returned)
+$endif
+
+int i, j, k, n1
+real val, wt, sumwt
+$if (datatype == sil)
+real sum, zero
+data zero /0.0/
+$else
+PIXEL sum, zero
+data zero /0$f/
+$endif
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Mem$t[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ do j = 2, n[i] {
+ val = max (zero, Mem$t[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = max (zero, Mem$t[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n[i] {
+ val = max (zero, Mem$t[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Mem$t[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ wt = wts[Memi[m[1]+k]]
+ sum = val * wt**2
+ sumwt = wt
+ do j = 2, n1 {
+ val = max (zero, Mem$t[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + val * wt**2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = max (zero, Mem$t[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = Mem$t[d[1]+k]**2
+ do j = 2, n1 {
+ val = max (zero, Mem$t[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] +
+ (val*nm[3,j])**2
+ sum = sum + val
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = max (zero, Mem$t[d[1]+k])
+ val = nm[1,1] + val/nm[2,1] + (val*nm[3,1])**2
+ sum = val
+ do j = 2, n1 {
+ val = max (zero, Mem$t[d[j]+k])
+ val = nm[1,j] + val/nm[2,j] + (val*nm[3,j])**2
+ sum = sum + val
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icomb.gx b/pkg/images/immatch/src/imcombine/src/icomb.gx
new file mode 100644
index 00000000..ae489158
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icomb.gx
@@ -0,0 +1,761 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include <pmset.h>
+include <error.h>
+include <syserr.h>
+include <mach.h>
+include "../icombine.h"
+
+# The following is for compiling under V2.11.
+define IM_BUFFRAC IM_BUFSIZE
+include <imset.h>
+
+
+# ICOMBINE -- Combine images
+#
+# The memory and open file descriptor limits are checked and an attempt
+# to recover is made either by setting the image pixel files to be
+# closed after I/O or by notifying the calling program that memory
+# ran out and the IMIO buffer size should be reduced. After the checks
+# a procedure for the selected combine option is called.
+# Because there may be several failure modes when reaching the file
+# limits we first assume an error is due to the file limit, except for
+# out of memory, and close some pixel files. If the error then repeats
+# on accessing the pixels the error is passed back.
+
+$for (sird)
+procedure icombine$t (in, out, scales, zeros, wts, offsets, nimages, bufsize)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+real wts[nimages] # Weights
+int offsets[nimages,ARB] # Input image offsets
+int nimages # Number of input images
+int bufsize # IMIO buffer size
+
+char str[1]
+int i, j, k, npts, fd, stropen(), xt_imgnl$t()
+pointer sp, d, id, n, m, lflag, v, dbuf
+pointer im, buf, xt_opix(), impl1i()
+errchk stropen, xt_cpix, xt_opix, xt_imgnl$t, impl1i, ic_combine$t
+$if (datatype == sil)
+pointer impl1r()
+errchk impl1r
+$else
+pointer impl1$t()
+errchk impl1$t
+$endif
+
+include "../icombine.com"
+
+begin
+ npts = IM_LEN(out[1],1)
+
+ # Allocate memory.
+ call smark (sp)
+ call salloc (dbuf, nimages, TY_POINTER)
+ call salloc (d, nimages, TY_POINTER)
+ call salloc (id, nimages, TY_POINTER)
+ call salloc (n, npts, TY_INT)
+ call salloc (m, nimages, TY_POINTER)
+ call salloc (lflag, nimages, TY_INT)
+ call salloc (v, IM_MAXDIM, TY_LONG)
+ call amovki (D_ALL, Memi[lflag], nimages)
+ call amovkl (1, Meml[v], IM_MAXDIM)
+
+ # If not aligned or growing create data buffers of output length
+ # otherwise use the IMIO buffers.
+
+ if (!aligned || grow >= 1.) {
+ do i = 1, nimages {
+ call salloc (Memi[dbuf+i-1], npts, TY_PIXEL)
+ call aclr$t (Mem$t[Memi[dbuf+i-1]], npts)
+ }
+ } else {
+ do i = 1, nimages {
+ im = xt_opix (in[i], i, 1)
+ if (im != in[i]) {
+ call salloc (Memi[dbuf+i-1], npts, TY_PIXEL)
+ call aclr$t (Mem$t[Memi[dbuf+i-1]], npts)
+ }
+ }
+ call amovki (NULL, Memi[dbuf], nimages)
+ }
+
+ if (project) {
+ call imseti (in[1], IM_NBUFS, nimages)
+ call imseti (in[1], IM_BUFFRAC, 0)
+ call imseti (in[1], IM_BUFSIZE, bufsize)
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ } else {
+ # Reserve FD for string operations.
+ fd = stropen (str, 1, NEW_FILE)
+
+ # Do I/O to the images.
+ do i = 1, 6 {
+ if (out[i] != NULL) {
+ call imseti (out[i], IM_BUFFRAC, 0)
+ call imseti (out[i], IM_BUFSIZE, bufsize)
+ }
+ }
+ $if (datatype == sil)
+ buf = impl1r (out[1])
+ call aclrr (Memr[buf], npts)
+ if (out[3] != NULL) {
+ buf = impl1r (out[3])
+ call aclrr (Memr[buf], npts)
+ }
+ $else
+ buf = impl1$t (out[1])
+ call aclr$t (Mem$t[buf], npts)
+ if (out[3] != NULL) {
+ buf = impl1$t (out[3])
+ call aclr$t (Mem$t[buf], npts)
+ }
+ $endif
+ if (out[2] != NULL) {
+ buf = impl1i (out[2])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[4] != NULL) {
+ buf = impl1i (out[4])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[5] != NULL) {
+ buf = impl1i (out[5])
+ call aclri (Memi[buf], npts)
+ }
+ if (out[6] != NULL) {
+ buf = impl1i (out[6])
+ call aclri (Memi[buf], npts)
+ }
+
+ # Do I/O for first input image line.
+ if (!project) {
+ do i = 1, nimages {
+ call xt_imseti (i, "bufsize", bufsize)
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ call xt_cpix (i)
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ call xt_cpix (i)
+ }
+
+ do i = 1, nimages {
+ j = max (0, offsets[i,1])
+ k = min (npts, IM_LEN(in[i],1) + offsets[i,1])
+ if (k - j < 1)
+ next
+ j = 1 - offsets[i,2]
+ if (j < 1 || j > IM_LEN(in[i],2))
+ next
+ iferr {
+ Meml[v+1] = j
+ j = xt_imgnl$t (in[i], i, buf, Meml[v], 1)
+ } then {
+ call imseti (im, IM_PIXFD, NULL)
+ call sfree (sp)
+ call strclose (fd)
+ call erract (EA_ERROR)
+ }
+ }
+ }
+
+ call strclose (fd)
+ }
+
+ call ic_combine$t (in, out, Memi[dbuf], Memi[d], Memi[id], Memi[n],
+ Memi[m], Memi[lflag], offsets, scales, zeros, wts, nimages, npts)
+end
+
+
+# IC_COMBINE -- Combine images.
+
+procedure ic_combine$t (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, wts, nimages, npts)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output image
+pointer dbuf[nimages] # Data buffers for nonaligned images
+pointer d[nimages] # Data pointers
+pointer id[nimages] # Image index ID pointers
+int n[npts] # Number of good pixels
+pointer m[nimages] # Mask pointers
+int lflag[nimages] # Line flags
+int offsets[nimages,ARB] # Input image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero offset factors
+real wts[nimages] # Combining weights
+int nimages # Number of input images
+int npts # Number of points per output line
+
+int i, ext, ctor(), errcode()
+real r, imgetr()
+pointer sp, fname, imname, v1, v2, v3, work
+pointer outdata, buf, nmod, nm, pms
+pointer immap(), impnli()
+$if (datatype == sil)
+pointer impnlr(), imgnlr()
+$else
+pointer impnl$t(), imgnl$t
+$endif
+errchk immap, ic_scale, imgetr, ic_grow, ic_grow$t, ic_rmasks, ic_emask
+errchk ic_gdata$t
+
+include "../icombine.com"
+data ext/0/
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (v3, IM_MAXDIM, TY_LONG)
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+
+ call ic_scale (in, out, offsets, scales, zeros, wts, nimages)
+
+ # Set combine parameters
+ switch (combine) {
+ case AVERAGE, SUM, QUAD, NMODEL:
+ if (dowts)
+ keepids = true
+ else
+ keepids = false
+ case MEDIAN:
+ dowts = false
+ keepids = false
+ }
+ docombine = true
+
+ # Get noise model parameters.
+ if (combine==NMODEL) {
+ call salloc (nmod, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nmod+3*(i-1)+1] = r * scales[i]
+ Memr[nmod+3*(i-1)] =
+ max ((Memr[nmod+3*(i-1)] / Memr[nmod+3*(i-1)+1]) ** 2,
+ 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nmod+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nmod+3*(i-1)+2] = r
+ }
+ }
+ }
+
+ # Set rejection algorithm specific parameters
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ call salloc (nm, 3*nimages, TY_REAL)
+ i = 1
+ if (ctor (Memc[rdnoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = r
+ } else {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)] = imgetr (in[i], Memc[rdnoise])
+ }
+ i = 1
+ if (ctor (Memc[gain], i, r) > 0) {
+ do i = 1, nimages {
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[gain])
+ Memr[nm+3*(i-1)+1] = r
+ Memr[nm+3*(i-1)] =
+ max ((Memr[nm+3*(i-1)] / r) ** 2, 1e4 / MAX_REAL)
+ }
+ }
+ i = 1
+ if (ctor (Memc[snoise], i, r) > 0) {
+ do i = 1, nimages
+ Memr[nm+3*(i-1)+2] = r
+ } else {
+ do i = 1, nimages {
+ r = imgetr (in[i], Memc[snoise])
+ Memr[nm+3*(i-1)+2] = r
+ }
+ }
+ if (!keepids) {
+ if (doscale1)
+ keepids = true
+ else {
+ do i = 2, nimages {
+ if (Memr[nm+3*(i-1)] != Memr[nm] ||
+ Memr[nm+3*(i-1)+1] != Memr[nm+1] ||
+ Memr[nm+3*(i-1)+2] != Memr[nm+2]) {
+ keepids = true
+ break
+ }
+ }
+ }
+ }
+ if (reject == CRREJECT)
+ lsigma = MAX_REAL
+ case MINMAX:
+ mclip = false
+ case PCLIP:
+ mclip = true
+ case AVSIGCLIP, SIGCLIP:
+ if (doscale1)
+ keepids = true
+ case NONE:
+ mclip = false
+ }
+
+ if (out[4] != NULL)
+ keepids = true
+
+ if (out[6] != NULL) {
+ keepids = true
+ call ic_einit (in, nimages, Memc[expkeyword], 1., 2**27-1)
+ }
+
+ if (grow >= 1.) {
+ keepids = true
+ call salloc (work, npts * nimages, TY_INT)
+ }
+ pms = NULL
+
+ if (keepids) {
+ do i = 1, nimages
+ call salloc (id[i], npts, TY_INT)
+ }
+
+# This idea turns out to has a problem with masks are used with wcs offsets.
+# the matching of masks to images based on WCS requires access to the WCS
+# of the images. For now we drop this idea but maybe a way can be identified
+# to know when this is not going to be needed.
+# # Reduce header memory use.
+# do i = 1, nimages
+# call xt_minhdr (i)
+
+ $if (datatype == sil)
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdata$t (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ if (mclip)
+ call ic_mccdclip$t (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ else
+ call ic_accdclip$t (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Memr[outdata])
+ case MINMAX:
+ call ic_mm$t (d, id, n, npts)
+ case PCLIP:
+ call ic_pclip$t (d, id, n, nimages, npts, Memr[outdata])
+ case SIGCLIP:
+ if (mclip)
+ call ic_msigclip$t (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ else
+ call ic_asigclip$t (d, id, n, scales, zeros, nimages, npts,
+ Memr[outdata])
+ case AVSIGCLIP:
+ if (mclip)
+ call ic_mavsigclip$t (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ else
+ call ic_aavsigclip$t (d, id, n, scales, zeros, nimages,
+ npts, Memr[outdata])
+ }
+
+ if (pms == NULL || nkeep > 0) {
+ if (docombine) {
+ switch (combine) {
+ case AVERAGE:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_median$t (d, n, npts, YES, Memr[outdata])
+ case SUM:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ YES, NO, Memr[outdata])
+ case QUAD:
+ call ic_quad$t (d, id, n, wts, nimages, npts,
+ YES, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodel$t (d, id, n, Memr[nmod], wts,
+ nimages, npts, YES, YES, Memr[outdata])
+ }
+ }
+ }
+
+ if (grow >= 1.)
+ call ic_grow (out, Meml[v2], id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (pms == NULL) {
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigma$t (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+ }
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+ $else
+ while (impnl$t (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdata$t (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ switch (reject) {
+ case CCDCLIP, CRREJECT:
+ if (mclip)
+ call ic_mccdclip$t (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Mem$t[outdata])
+ else
+ call ic_accdclip$t (d, id, n, scales, zeros, Memr[nm],
+ nimages, npts, Mem$t[outdata])
+ case MINMAX:
+ call ic_mm$t (d, id, n, npts)
+ case PCLIP:
+ call ic_pclip$t (d, id, n, nimages, npts, Mem$t[outdata])
+ case SIGCLIP:
+ if (mclip)
+ call ic_msigclip$t (d, id, n, scales, zeros, nimages, npts,
+ Mem$t[outdata])
+ else
+ call ic_asigclip$t (d, id, n, scales, zeros, nimages, npts,
+ Mem$t[outdata])
+ case AVSIGCLIP:
+ if (mclip)
+ call ic_mavsigclip$t (d, id, n, scales, zeros, nimages,
+ npts, Mem$t[outdata])
+ else
+ call ic_aavsigclip$t (d, id, n, scales, zeros, nimages,
+ npts, Mem$t[outdata])
+ }
+
+ if (pms == NULL || nkeep > 0) {
+ if (docombine) {
+ switch (combine) {
+ case AVERAGE:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ YES, YES, Mem$t[outdata])
+ case MEDIAN:
+ call ic_median$t (d, n, npts, YES, Mem$t[outdata])
+ case SUM:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ YES, NO, Mem$t[outdata])
+ case QUAD:
+ call ic_quad$t (d, id, n, wts, nimages, npts,
+ YES, YES, Mem$t[outdata])
+ case NMODEL:
+ call ic_nmodel$t (d, id, n, Memr[nmod], wts,
+ nimages, npts, YES, YES, Mem$t[outdata])
+ }
+ }
+ }
+
+ if (grow >= 1.)
+ call ic_grow (out, Meml[v2], id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (pms == NULL) {
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnl$t (out[3], buf, Meml[v1])
+ call ic_sigma$t (d, id, n, wts, npts, Mem$t[outdata],
+ Mem$t[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+ }
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+ $endif
+
+ if (pms != NULL) {
+ if (nkeep > 0) {
+ call imstats (out[1], IM_IMAGENAME, Memc[fname], SZ_FNAME)
+ call imunmap (out[1])
+ iferr (buf = immap (Memc[fname], READ_WRITE, 0)) {
+ switch (errcode()) {
+ case SYS_FXFOPNOEXTNV:
+ call imgcluster (Memc[fname], Memc[fname], SZ_FNAME)
+ ext = ext + 1
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext)
+ iferr (buf = immap (Memc[imname], READ_WRITE, 0)) {
+ buf = NULL
+ ext = 0
+ }
+ repeat {
+ call sprintf (Memc[imname], SZ_FNAME, "%s[%d]")
+ call pargstr (Memc[fname])
+ call pargi (ext+1)
+ iferr (outdata = immap (Memc[imname],READ_WRITE,0))
+ break
+ if (buf != NULL)
+ call imunmap (buf)
+ buf = outdata
+ ext = ext + 1
+ }
+ default:
+ call erract (EA_ERROR)
+ }
+ }
+ out[1] = buf
+ }
+
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ call amovkl (long(1), Meml[v3], IM_MAXDIM)
+ $if (datatype == sil)
+ while (impnlr (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdata$t (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ call ic_grow$t (Meml[v2], d, id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (nkeep > 0) {
+ do i = 1, npts {
+ if (n[i] < nkeep) {
+ Meml[v1+1] = Meml[v1+1] - 1
+ if (imgnlr (out[1], buf, Meml[v1]) == EOF)
+ ;
+ call amovr (Memr[buf], Memr[outdata], npts)
+ break
+ }
+ }
+ }
+
+ switch (combine) {
+ case AVERAGE:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case MEDIAN:
+ call ic_median$t (d, n, npts, NO, Memr[outdata])
+ case SUM:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ NO, NO, Memr[outdata])
+ case QUAD:
+ call ic_quad$t (d, id, n, wts, nimages, npts,
+ NO, YES, Memr[outdata])
+ case NMODEL:
+ call ic_nmodel$t (d, id, n, Memr[nmod], wts,
+ nimages, npts, NO, YES, Memr[outdata])
+ }
+
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnlr (out[3], buf, Meml[v1])
+ call ic_sigma$t (d, id, n, wts, npts, Memr[outdata],
+ Memr[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+ $else
+ while (impnl$t (out[1], outdata, Meml[v1]) != EOF) {
+ call ic_gdata$t (in, out, dbuf, d, id, n, m, lflag, offsets,
+ scales, zeros, nimages, npts, Meml[v2], Meml[v3])
+
+ call ic_grow$t (Meml[v2], d, id, n, Memi[work], nimages, npts,
+ pms)
+
+ if (nkeep > 0) {
+ do i = 1, npts {
+ if (n[i] < nkeep) {
+ Meml[v1+1] = Meml[v1+1] - 1
+ if (imgnl$t (out[1], buf, Meml[v1]) == EOF)
+ ;
+ call amov$t (Mem$t[buf], Mem$t[outdata], npts)
+ break
+ }
+ }
+ }
+
+ switch (combine) {
+ case AVERAGE:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ NO, YES, Mem$t[outdata])
+ case MEDIAN:
+ call ic_median$t (d, n, npts, NO, Mem$t[outdata])
+ case SUM:
+ call ic_average$t (d, id, n, wts, nimages, npts,
+ NO, NO, Mem$t[outdata])
+ case QUAD:
+ call ic_quad$t (d, id, n, wts, nimages, npts,
+ NO, YES, Mem$t[outdata])
+ case NMODEL:
+ call ic_nmodel$t (d, id, n, Memr[nmod], wts,
+ nimages, npts, NO, YES, Mem$t[outdata])
+ }
+
+ if (out[2] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[2], buf, Meml[v1])
+ do i = 1, npts {
+ if (n[i] > 0)
+ Memi[buf] = 0
+ else if (n[i] == 0)
+ Memi[buf] = 1
+ else
+ Memi[buf] = 2
+ buf = buf + 1
+ }
+ }
+
+ if (out[3] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnl$t (out[3], buf, Meml[v1])
+ call ic_sigma$t (d, id, n, wts, npts, Mem$t[outdata],
+ Mem$t[buf])
+ }
+
+ if (out[4] != NULL)
+ call ic_rmasks (out[4], Meml[v2], id, nimages, n, npts)
+
+ if (out[5] != NULL) {
+ call amovl (Meml[v2], Meml[v1], IM_MAXDIM)
+ i = impnli (out[5], buf, Meml[v1])
+ call amovki (nimages, Memi[buf], npts)
+ call asubi (Memi[buf], n, Memi[buf], npts)
+ }
+
+ if (out[6] != NULL)
+ call ic_emask (out[6], Meml[v2], id, nimages, n, wts, npts)
+
+ call amovl (Meml[v1], Meml[v2], IM_MAXDIM)
+ }
+ $endif
+
+ do i = 1, nimages
+ call pm_close (Memi[pms+i-1])
+ call mfree (pms, TY_POINTER)
+ }
+
+ call sfree (sp)
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icombine.com b/pkg/images/immatch/src/imcombine/src/icombine.com
new file mode 100644
index 00000000..55ad308b
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icombine.com
@@ -0,0 +1,45 @@
+# ICOMBINE Common
+
+int combine # Combine algorithm
+int medtype # Median type
+int reject # Rejection algorithm
+bool project # Combine across the highest dimension?
+real blank # Blank value
+pointer expkeyword # Exposure time keyword
+pointer statsec # Statistics section
+pointer rdnoise # CCD read noise
+pointer gain # CCD gain
+pointer snoise # CCD sensitivity noise
+real lthresh # Low threshold
+real hthresh # High threshold
+int nkeep # Minimum to keep
+real lsigma # Low sigma cutoff
+real hsigma # High sigma cutoff
+real pclip # Number or fraction of pixels from median
+real flow # Fraction of low pixels to reject
+real fhigh # Fraction of high pixels to reject
+real grow # Grow radius
+bool mclip # Use median in sigma clipping?
+real sigscale # Sigma scaling tolerance
+int logfd # Log file descriptor
+
+# These flags allow special conditions to be optimized.
+
+int dflag # Data flag (D_ALL, D_NONE, D_MIX)
+bool aligned # Are the images aligned?
+bool doscale # Do the images have to be scaled?
+bool doscale1 # Do the sigma calculations have to be scaled?
+bool dothresh # Check pixels outside specified thresholds?
+bool dowts # Does the final average have to be weighted?
+bool keepids # Keep track of the image indices?
+bool docombine # Call the combine procedure?
+bool sort # Sort data?
+bool verbose # Verbose?
+
+pointer icm # Mask data structure
+
+common /imccom/ combine, medtype, reject, blank, expkeyword, statsec, rdnoise,
+ gain, snoise, lsigma, hsigma, lthresh, hthresh, nkeep,
+ pclip, flow, fhigh, grow, logfd, dflag, sigscale, project,
+ mclip, aligned, doscale, doscale1, dothresh, dowts,
+ keepids, docombine, sort, verbose, icm
diff --git a/pkg/images/immatch/src/imcombine/src/icombine.h b/pkg/images/immatch/src/imcombine/src/icombine.h
new file mode 100644
index 00000000..51f60887
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icombine.h
@@ -0,0 +1,63 @@
+# ICOMBINE Definitions
+
+# Memory management parameters;
+define MAXMEMORY 500000000 # maximum memory
+define FUDGE 0.8 # fudge factor
+
+# Rejection options:
+define REJECT "|none|ccdclip|crreject|minmax|pclip|sigclip|avsigclip|"
+define NONE 1 # No rejection algorithm
+define CCDCLIP 2 # CCD noise function clipping
+define CRREJECT 3 # CCD noise function clipping
+define MINMAX 4 # Minmax rejection
+define PCLIP 5 # Percentile clip
+define SIGCLIP 6 # Sigma clip
+define AVSIGCLIP 7 # Sigma clip with average poisson sigma
+
+# Combine options:
+define COMBINE "|average|median|lmedian|sum|quadrature|nmodel|"
+define AVERAGE 1
+define MEDIAN 2
+define LMEDIAN 3
+define SUM 4
+define QUAD 5
+define NMODEL 6
+
+# Median types:
+define MEDAVG 1 # Central average for even N
+define MEDLOW 2 # Lower value for even N
+
+# Scaling options:
+define STYPES "|none|mode|median|mean|exposure|"
+define ZTYPES "|none|mode|median|mean|"
+define WTYPES "|none|mode|median|mean|exposure|"
+define S_NONE 1
+define S_MODE 2
+define S_MEDIAN 3
+define S_MEAN 4
+define S_EXPOSURE 5
+define S_FILE 6
+define S_KEYWORD 7
+define S_SECTION "|input|output|overlap|"
+define S_INPUT 1
+define S_OUTPUT 2
+define S_OVERLAP 3
+
+# Mask options
+define MASKTYPES "|none|goodvalue|badvalue|goodbits|badbits|novalue|"
+define M_NONE 1 # Don't use mask images
+define M_GOODVAL 2 # Value selecting good pixels
+define M_BADVAL 3 # Value selecting bad pixels
+define M_GOODBITS 4 # Bits selecting good pixels
+define M_BADBITS 5 # Bits selecting bad pixels
+define M_NOVAL 6 # Value selecting no value (good = 0)
+define M_LTVAL 7 # Values less than specified are good
+define M_GTVAL 8 # Values greater than specified are good
+define M_BOOLEAN -1 # Ignore mask values
+
+# Data flag
+define D_ALL 0 # All pixels are good
+define D_NONE 1 # All pixels are bad or rejected
+define D_MIX 2 # Mixture of good and bad pixels
+
+define TOL 0.001 # Tolerance for equal residuals
diff --git a/pkg/images/immatch/src/imcombine/src/icombine.x b/pkg/images/immatch/src/imcombine/src/icombine.x
new file mode 100644
index 00000000..b6e5ddd4
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icombine.x
@@ -0,0 +1,520 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include <error.h>
+include <syserr.h>
+include "icombine.h"
+
+
+# ICOMBINE -- Combine input list or image.
+# This procedure maps the images, sets the output dimensions and datatype,
+# opens the logfile, and sets IMIO parameters. It attempts to adjust
+# buffer sizes and memory requirements for maximum efficiency.
+
+procedure icombine (list, output, headers, bmask, rmask, nrmask, emask,
+ sigma, logfile, scales, zeros, wts, stack, delete, listonly)
+
+int list #I List of input images
+char output[ARB] #I Output image
+char headers[ARB] #I Output header rootname
+char bmask[ARB] #I Bad pixel mask
+char rmask[ARB] #I Rejection mask
+char nrmask[ARB] #I Nreject mask
+char emask[ARB] #I Exposure mask
+char sigma[ARB] #I Sigma image (optional)
+char logfile[ARB] #I Logfile (optional)
+real scales[ARB] #I Scale factors
+real zeros[ARB] #I Offset factors
+real wts[ARB] #I Weights
+int stack #I Stack input images?
+int delete #I Delete input images?
+int listonly #I List images to combine?
+
+bool proj
+char input[SZ_FNAME], errstr[SZ_LINE]
+int i, j, nimages, intype, bufsize, oldsize, stack1, err, retry
+int maxsize, maxmemory, memory
+pointer sp, im, in1, in, out[6], offsets, key, tmp, bpmstack
+
+char clgetc()
+int clgwrd(), imtlen(), imtgetim(), imtrgetim(), getdatatype(), envgeti()
+int begmem(), errget(), open(), ty_max(), sizeof(), strmatch()
+pointer immap(), xt_immap(), ic_pmmap()
+errchk ic_imstack, immap, imunmap, xt_immap, ic_pmmap, ic_setout
+
+include "icombine.com"
+
+define retry_ 98
+define err_ 99
+
+begin
+ if (listonly == YES) {
+ # Write the output list.
+ if (output[1] == EOS) {
+ call imtrew (list)
+ while (imtgetim (list, input, SZ_FNAME)!=EOF) {
+ i = strmatch (input, "[0]") - 3
+ if (i > 0)
+ call strcpy (input[i+3], input[i], SZ_FNAME)
+ call printf ("%s\n")
+ call pargstr (input)
+ }
+ } else {
+ call sprintf (errstr, SZ_LINE, "%s.list")
+ call pargstr (output)
+ iferr (logfd = open (errstr, APPEND, TEXT_FILE))
+ call erract (EA_WARN)
+ call imtrew (list)
+ while (imtgetim (list, input, SZ_FNAME)!=EOF) {
+ i = strmatch (input, "[0]") - 3
+ if (i > 0)
+ call strcpy (input[i+3], input[i], SZ_FNAME)
+ call printf ("%s -> %s\n")
+ call pargstr (input)
+ call pargstr (errstr)
+ call fprintf (logfd, "%s\n")
+ call pargstr (input)
+ }
+ call close (logfd)
+ }
+ return
+ }
+
+ nimages = imtlen (list)
+ if (nimages == 0)
+ call error (1, "No images to combine")
+
+ if (project) {
+ if (imtgetim (list, input, SZ_FNAME) == EOF)
+ call error (1, "No image to project")
+ }
+
+ bufsize = 0
+# if (nimages > LAST_FD - 15)
+# stack1 = YES
+# else
+ stack1 = stack
+
+ retry = 0
+
+retry_
+ iferr {
+ call smark (sp)
+ call salloc (in, 1, TY_POINTER)
+
+ nimages = 0
+ in1 = NULL; Memi[in] = NULL; logfd = NULL
+ out[1] = NULL; out[2] = NULL; out[3] = NULL
+ out[4] = NULL; out[5] = NULL; out[6] = NULL
+
+ # Stack the input images.
+ if (stack1 == YES) {
+ proj = project
+ project = true
+ call salloc (bpmstack, SZ_FNAME, TY_CHAR)
+ i = clgwrd ("masktype", Memc[bpmstack], SZ_FNAME, MASKTYPES)
+ if (i == M_NONE)
+ Memc[bpmstack] = EOS
+ else {
+ call mktemp ("tmp", Memc[bpmstack], SZ_FNAME)
+ call strcat (".pl", Memc[bpmstack], SZ_FNAME)
+ }
+ call mktemp ("tmp", input, SZ_FNAME)
+ call imtrew (list)
+ call ic_imstack (list, input, Memc[bpmstack])
+ }
+
+ # Open the input image(s).
+ if (project) {
+ tmp = immap (input, READ_ONLY, 0); out[1] = tmp
+ if (IM_NDIM(out[1]) == 1)
+ call error (1, "Can't project one dimensional images")
+ nimages = IM_LEN(out[1],IM_NDIM(out[1]))
+ call salloc (in, nimages, TY_POINTER)
+ call amovki (out[1], Memi[in], nimages)
+ } else {
+ call salloc (in, imtlen(list), TY_POINTER)
+ call amovki (NULL, Memi[in], imtlen(list))
+ call imtrew (list)
+ while (imtgetim (list, input, SZ_FNAME)!=EOF) {
+ nimages = nimages + 1
+ tmp = xt_immap (input, READ_ONLY, 0, nimages, retry)
+ Memi[in+nimages-1] = tmp
+ }
+
+ # Check sizes and set I/O option.
+ intype = 0
+ tmp = Memi[in]
+ do i = 2, nimages {
+ do j = 1, IM_NDIM(tmp) {
+ if (IM_LEN(tmp,j) != IM_LEN(Memi[in+i-1],j))
+ intype = 1
+ }
+ if (intype == 1)
+ break
+ }
+ if (intype == 1)
+ call xt_imseti (0, "option", intype)
+ }
+
+ # Check if there are no images.
+ if (nimages == 0)
+ call error (1, "No images to combine")
+
+ # Convert the pclip parameter to a number of pixels rather than
+ # a fraction. This number stays constant even if pixels are
+ # rejected. The number of low and high pixel rejected, however,
+ # are converted to a fraction of the valid pixels.
+
+ if (reject == PCLIP) {
+ i = nimages / 2.
+ if (abs (pclip) < 1.)
+ pclip = pclip * i
+ if (pclip < 0.)
+ pclip = min (-1, max (-i, int (pclip)))
+ else
+ pclip = max (1, min (i, int (pclip)))
+ }
+
+ if (reject == MINMAX) {
+ if (flow >= 1)
+ flow = flow / nimages
+ if (fhigh >= 1)
+ fhigh = fhigh / nimages
+ i = flow * nimages
+ j = fhigh * nimages
+ if (i + j == 0)
+ reject = NONE
+ else if (i + j >= nimages)
+ call error (1, "Bad minmax rejection parameters")
+ }
+
+ # Map the output image and set dimensions and offsets.
+ if (stack1 == YES) {
+ call imtrew (list)
+ i = imtgetim (list, errstr, SZ_LINE)
+ in1 = immap (errstr, READ_ONLY, 0)
+ tmp = immap (output, NEW_COPY, in1); out[1] = tmp
+ call salloc (key, SZ_FNAME, TY_CHAR)
+ do i = 1, nimages {
+ call sprintf (Memc[key], SZ_FNAME, "stck%04d")
+ call pargi (i)
+ iferr (call imdelf (out[1], Memc[key]))
+ ;
+ if (Memc[bpmstack] != EOS) {
+ call sprintf (Memc[key], SZ_FNAME, "bpm%04d")
+ call pargi (i)
+ iferr (call imdelf (out[1], Memc[key]))
+ ;
+ }
+ }
+ } else {
+ tmp = immap (output, NEW_COPY, Memi[in]); out[1] = tmp
+ if (project) {
+ IM_LEN(out[1],IM_NDIM(out[1])) = 1
+ IM_NDIM(out[1]) = IM_NDIM(out[1]) - 1
+ }
+ }
+ call salloc (offsets, nimages*IM_NDIM(out[1]), TY_INT)
+ iferr (call ic_setout (Memi[in], out, Memi[offsets], nimages)) {
+ call erract (EA_WARN)
+ call error (1, "Can't set output geometry")
+ }
+ call ic_hdr (Memi[in], out, nimages)
+ iferr (call imdelf (out, "BPM"))
+ ;
+
+ # Determine the highest precedence datatype and set output datatype.
+ intype = IM_PIXTYPE(Memi[in])
+ do i = 2, nimages
+ intype = ty_max (intype, IM_PIXTYPE(Memi[in+i-1]))
+ IM_PIXTYPE(out[1]) = getdatatype (clgetc ("outtype"))
+ if (IM_PIXTYPE(out[1]) == ERR)
+ IM_PIXTYPE(out[1]) = intype
+
+ # Open rejection masks
+ if (rmask[1] != EOS) {
+ tmp = ic_pmmap (rmask, NEW_COPY, out[1]); out[4] = tmp
+ IM_NDIM(out[4]) = IM_NDIM(out[4]) + 1
+ IM_LEN(out[4],IM_NDIM(out[4])) = nimages
+ if (!project) {
+ if (key == NULL)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+ do i = 100, nimages {
+ j = imtrgetim (list, i, input, SZ_FNAME)
+ if (i < 999)
+ call sprintf (Memc[key], SZ_FNAME, "imcmb%d")
+ else if (i < 9999)
+ call sprintf (Memc[key], SZ_FNAME, "imcm%d")
+ else
+ call sprintf (Memc[key], SZ_FNAME, "imc%d")
+ call pargi (i)
+ call imastr (out[4], Memc[key], input)
+ }
+ }
+ } else
+ out[4] = NULL
+
+ # Open bad pixel pixel list file if given.
+ if (bmask[1] != EOS) {
+ tmp = ic_pmmap (bmask, NEW_COPY, out[1]); out[2] = tmp
+ } else
+ out[2] = NULL
+
+ # Open nreject pixel list file if given.
+ if (nrmask[1] != EOS) {
+ tmp = ic_pmmap (nrmask, NEW_COPY, out[1]); out[5] = tmp
+ } else
+ out[5] = NULL
+
+ # Open exposure mask if given.
+ if (emask[1] != EOS) {
+ tmp = ic_pmmap (emask, NEW_COPY, out[1]); out[6] = tmp
+ } else
+ out[6] = NULL
+
+ # Open the sigma image if given.
+ if (sigma[1] != EOS) {
+ tmp = immap (sigma, NEW_COPY, out[1]); out[3] = tmp
+ IM_PIXTYPE(out[3]) = ty_max (TY_REAL, IM_PIXTYPE(out[1]))
+ call sprintf (IM_TITLE(out[3]), SZ_IMTITLE,
+ "Combine sigma images for %s")
+ call pargstr (output)
+ } else
+ out[3] = NULL
+
+ # Open masks.
+ call ic_mopen (Memi[in], out, nimages, Memi[offsets],
+ min(retry,1))
+
+ # Open the log file.
+ logfd = NULL
+ if (logfile[1] != EOS) {
+ iferr (logfd = open (logfile, APPEND, TEXT_FILE)) {
+ logfd = NULL
+ call erract (EA_WARN)
+ }
+ }
+
+ if (bufsize == 0) {
+ # Set initial IMIO buffer size based on the number of images
+ # and maximum amount of working memory available. The buffer
+ # size may be adjusted later if the task runs out of memory.
+ # The FUDGE factor is used to allow for the size of the
+ # program, memory allocator inefficiencies, and any other
+ # memory requirements besides IMIO.
+
+ iferr (maxmemory = envgeti ("imcombine_maxmemory"))
+ maxmemory = MAXMEMORY
+ memory = begmem (0, oldsize, maxsize)
+ memory = min (memory, maxsize, maxmemory)
+ bufsize = FUDGE * memory / (nimages + 1) / sizeof (intype)
+ }
+
+ # Combine the images. If an out of memory error occurs close all
+ # images and files, divide the IMIO buffer size in half and try
+ # again.
+
+ switch (ty_max (intype, IM_PIXTYPE(out[1]))) {
+ case TY_SHORT:
+ call icombines (Memi[in], out, scales, zeros,
+ wts, Memi[offsets], nimages, bufsize)
+ case TY_USHORT, TY_INT, TY_LONG:
+ call icombinei (Memi[in], out, scales, zeros,
+ wts, Memi[offsets], nimages, bufsize)
+ case TY_DOUBLE:
+ call icombined (Memi[in], out, scales, zeros,
+ wts, Memi[offsets], nimages, bufsize)
+ case TY_COMPLEX:
+ call error (1, "Complex images not allowed")
+ default:
+ call icombiner (Memi[in], out, scales, zeros,
+ wts, Memi[offsets], nimages, bufsize)
+ }
+ } then {
+ err = errget (errstr, SZ_LINE)
+ if (err == SYS_IKIOPIX && nimages < 250)
+ err = SYS_MFULL
+ call ic_mclose (nimages)
+ if (!project) {
+ do j = 2, nimages {
+ if (Memi[in+j-1] != NULL)
+ call xt_imunmap (Memi[in+j-1], j)
+ }
+ }
+ if (out[2] != NULL) {
+ iferr (call imunmap (out[2]))
+ ;
+ iferr (call imdelete (bmask))
+ ;
+ }
+ if (out[3] != NULL) {
+ iferr (call imunmap (out[3]))
+ ;
+ iferr (call imdelete (sigma))
+ ;
+ }
+ if (out[4] != NULL) {
+ iferr (call imunmap (out[4]))
+ ;
+ iferr (call imdelete (rmask))
+ ;
+ }
+ if (out[5] != NULL) {
+ iferr (call imunmap (out[5]))
+ ;
+ iferr (call imdelete (nrmask))
+ ;
+ }
+ if (out[6] != NULL) {
+ iferr (call imunmap (out[6]))
+ ;
+ iferr (call imdelete (emask))
+ ;
+ }
+ if (out[1] != NULL) {
+ iferr (call imunmap (out[1]))
+ ;
+ iferr (call imdelete (output))
+ ;
+ }
+ if (Memi[in] != NULL)
+ call xt_imunmap (Memi[in], 1)
+ if (in1 != NULL)
+ call imunmap (in1)
+ if (logfd != NULL)
+ call close (logfd)
+
+ switch (err) {
+ case SYS_MFULL:
+ if (project)
+ goto err_
+
+ if (bufsize < 10000 && retry > 2) {
+ call strcat ("- Maybe min_lenuserarea is too large",
+ errstr, SZ_LINE)
+ goto err_
+ }
+
+ bufsize = bufsize / 2
+ retry = retry + 1
+ call sfree (sp)
+ goto retry_
+ case SYS_FTOOMANYFILES, SYS_IKIOPEN, SYS_IKIOPIX, SYS_FOPEN, SYS_FWTNOACC:
+ if (project)
+ goto err_
+ stack1 = YES
+ call sfree (sp)
+ goto retry_
+ default:
+err_
+ if (stack1 == YES) {
+ iferr (call imdelete (input))
+ ;
+ if (Memc[bpmstack] != EOS) {
+ iferr (call imdelete (Memc[bpmstack]))
+ ;
+ }
+ }
+ call fixmem (oldsize)
+ while (imtgetim (list, input, SZ_FNAME)!=EOF)
+ ;
+ call sfree (sp)
+ call error (err, errstr)
+ }
+ }
+
+ # Unmap all the images, close the log file, and restore memory.
+ if (out[2] != NULL)
+ iferr (call imunmap (out[2]))
+ call erract (EA_WARN)
+ if (out[3] != NULL)
+ iferr (call imunmap (out[3]))
+ call erract (EA_WARN)
+ if (out[4] != NULL) {
+ # Close the output first so that there is no confusion with
+ # inheriting the output header. Then update the WCS for the
+ # extra dimension. Note that this may not be correct with
+ # axis reduced WCS.
+ iferr {
+ call imunmap (out[4])
+ out[4] = immap (rmask, READ_WRITE, 0)
+ i = IM_NDIM(out[4])
+ call imaddi (out[4], "WCSDIM", i)
+ call sprintf (errstr, SZ_LINE, "LTM%d_%d")
+ call pargi (i)
+ call pargi (i)
+ call imaddr (out[4], errstr, 1.)
+ call sprintf (errstr, SZ_LINE, "CD%d_%d")
+ call pargi (i)
+ call pargi (i)
+ call imaddr (out[4], errstr, 1.)
+ call imunmap (out[4])
+ } then
+ call erract (EA_WARN)
+ }
+ if (out[5] != NULL)
+ iferr (call imunmap (out[5]))
+ call erract (EA_WARN)
+ if (out[6] != NULL)
+ iferr (call imunmap (out[6]))
+ call erract (EA_WARN)
+ if (out[1] != NULL) {
+ call imunmap (out[1])
+ if (headers[1] != EOS) {
+ # Write input headers to a multiextension file if desired.
+ # This might be the same as the output image.
+ iferr {
+ do i = 1, nimages {
+ im = Memi[in+i-1]
+ call imstats (im, IM_IMAGENAME, input, SZ_FNAME)
+ if (strmatch (headers, ".fits$") == 0) {
+ call sprintf (errstr, SZ_LINE, "%s.fits[append]")
+ call pargstr (headers)
+ } else {
+ call sprintf (errstr, SZ_LINE, "%s[append]")
+ call pargstr (headers)
+ }
+ tmp = immap (errstr, NEW_COPY, im)
+ IM_NDIM(tmp) = 0
+ do j = 1, IM_NDIM(im) {
+ call sprintf (errstr, SZ_LINE, "AXLEN%d")
+ call pargi (j)
+ call imaddi (tmp, errstr, IM_LEN(im,j))
+ }
+ call imastr (tmp, "INIMAGE", input)
+ call imastr (tmp, "OUTIMAGE", output)
+ call imastr (tmp, "EXTNAME", input)
+ call imunmap (tmp)
+ }
+ if (logfd != NULL) {
+ call eprintf (" Headers = %s\n")
+ call pargstr (headers)
+ }
+ } then
+ call erract (EA_WARN)
+ }
+ }
+ if (!project) {
+ do i = 2, nimages {
+ if (Memi[in+i-1] != NULL)
+ call xt_imunmap (Memi[in+i-1], i)
+ }
+ }
+ if (Memi[in] != NULL)
+ call xt_imunmap (Memi[in], 1)
+ if (in1 != NULL)
+ call imunmap (in1)
+ if (stack1 == YES) {
+ call imdelete (input)
+ if (Memc[bpmstack] != EOS)
+ call imdelete (Memc[bpmstack])
+ project = proj
+ }
+ if (logfd != NULL)
+ call close (logfd)
+ call ic_mclose (nimages)
+ call fixmem (oldsize)
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icpclip.gx b/pkg/images/immatch/src/imcombine/src/icpclip.gx
new file mode 100644
index 00000000..628dca0d
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icpclip.gx
@@ -0,0 +1,233 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 3 # Minimum number for clipping
+
+$for (sird)
+# IC_PCLIP -- Percentile clip
+#
+# 1) Find the median
+# 2) Find the pixel which is the specified order index away
+# 3) Use the data value difference as a sigma and apply clipping
+# 4) Since the median is known return it so it does not have to be recomputed
+
+procedure ic_pclip$t (d, m, n, nimages, npts, median)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image id pointers
+int n[npts] # Number of good pixels
+int nimages # Number of input images
+int npts # Number of output points per line
+$if (datatype == sil)
+real median[npts] # Median
+$else
+PIXEL median[npts] # Median
+$endif
+
+int i, j, k, l, id, n1, n2, n3, n4, n5, nl, nh, nin, maxkeep
+bool even, fp_equalr()
+real sigma, r, s, t
+pointer sp, resid, mp1, mp2
+$if (datatype == sil)
+real med
+$else
+PIXEL med
+$endif
+
+include "../icombine.com"
+
+begin
+ # There must be at least MINCLIP and more than nkeep pixels.
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+
+ # Set sign of pclip parameter
+ if (pclip < 0)
+ t = -1.
+ else
+ t = 1.
+
+ # If there are no rejected pixels compute certain parameters once.
+ if (dflag == D_ALL) {
+ n1 = max (0, n[1])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0.) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ nin = n1
+ }
+
+ # Now apply clipping.
+ do i = 1, npts {
+ # Compute median.
+ if (dflag == D_MIX) {
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ if (n1 == 0) {
+ if (combine == MEDIAN)
+ median[i] = blank
+ next
+ }
+ n2 = 1 + n1 / 2
+ even = (mod (n1, 2) == 0)
+ if (pclip < 0) {
+ if (even)
+ n3 = max (1, nint (n2 - 1 + pclip))
+ else
+ n3 = max (1, nint (n2 + pclip))
+ } else
+ n3 = min (n1, nint (n2 + pclip))
+ }
+
+ j = i - 1
+ if (even) {
+ med = Mem$t[d[n2-1]+j]
+ med = (med + Mem$t[d[n2]+j]) / 2.
+ } else
+ med = Mem$t[d[n2]+j]
+
+ if (n1 < max (MINCLIP, maxkeep+1)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Define sigma for clipping
+ sigma = t * (Mem$t[d[n3]+j] - med)
+ if (fp_equalr (sigma, 0.)) {
+ if (combine == MEDIAN)
+ median[i] = med
+ next
+ }
+
+ # Reject pixels and save residuals.
+ # Check if any pixels are clipped.
+ # If so recompute the median and reset the number of good pixels.
+ # Only reorder if needed.
+
+ for (nl=1; nl<=n1; nl=nl+1) {
+ r = (med - Mem$t[d[nl]+j]) / sigma
+ if (r < lsigma)
+ break
+ Memr[resid+nl] = r
+ }
+ for (nh=n1; nh>=1; nh=nh-1) {
+ r = (Mem$t[d[nh]+j] - med) / sigma
+ if (r < hsigma)
+ break
+ Memr[resid+nh] = r
+ }
+ n4 = nh - nl + 1
+
+ # If too many pixels are rejected add some back in.
+ # All pixels with the same residual are added.
+ while (n4 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n4 = nh - nl + 1
+ }
+
+ # If any pixels are rejected recompute the median.
+ if (nl > 1 || nh < n1) {
+ n5 = nl + n4 / 2
+ if (mod (n4, 2) == 0) {
+ med = Mem$t[d[n5-1]+j]
+ med = (med + Mem$t[d[n5]+j]) / 2.
+ } else
+ med = Mem$t[d[n5]+j]
+ n[i] = n4
+ }
+ if (combine == MEDIAN)
+ median[i] = med
+
+ # Reorder if pixels only if necessary.
+ if (nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ k = max (nl, n4 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mem$t[d[l]+j] = Mem$t[d[k]+j]
+ if (grow >= 1.) {
+ mp1 = m[l] + j
+ mp2 = m[k] + j
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+j] = Memi[m[k]+j]
+ k = k + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mem$t[d[l]+j] = Mem$t[d[k]+j]
+ k = k + 1
+ }
+ }
+ }
+ }
+
+ # Check if data flag needs to be reset for rejected pixels.
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag whether the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icpmmap.x b/pkg/images/immatch/src/imcombine/src/icpmmap.x
new file mode 100644
index 00000000..1afeedd7
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icpmmap.x
@@ -0,0 +1,34 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <pmset.h>
+
+
+# IC_PMMAP -- Map pixel mask.
+
+pointer procedure ic_pmmap (fname, mode, refim)
+
+char fname[ARB] # Mask name
+int mode # Image mode
+pointer refim # Reference image
+pointer pm # IMIO pointer (returned)
+
+int i, fnextn()
+pointer sp, extn, immap()
+bool streq()
+
+begin
+ call smark (sp)
+ call salloc (extn, SZ_FNAME, TY_CHAR)
+
+ i = fnextn (fname, Memc[extn], SZ_FNAME)
+ if (streq (Memc[extn], "pl"))
+ pm = immap (fname, mode, refim)
+ else {
+ call strcpy (fname, Memc[extn], SZ_FNAME)
+ call strcat (".pl", Memc[extn], SZ_FNAME)
+ pm = immap (Memc[extn], mode, refim)
+ }
+
+ call sfree (sp)
+ return (pm)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icquad.gx b/pkg/images/immatch/src/imcombine/src/icquad.gx
new file mode 100644
index 00000000..4ecf3aa0
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icquad.gx
@@ -0,0 +1,133 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <mach.h>
+include "../icombine.h"
+include "../icmask.h"
+
+$for (sird)
+# IC_QUAD -- Compute the quadrature average (or summed) image line.
+# Options include a weighted average/sum.
+
+procedure ic_quad$t (d, m, n, wts, nimages, npts, doblank, doaverage,
+ average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image ID pointers
+int n[npts] # Number of points
+real wts[nimages] # Weights
+int nimages # Number of images
+int npts # Number of output points per line
+int doblank # Set blank values?
+int doaverage # Do average?
+$if (datatype == sil)
+real average[npts] # Average (returned)
+$else
+PIXEL average[npts] # Average (returned)
+$endif
+
+int i, j, k, n1
+real val, wt, sumwt
+$if (datatype == sil)
+real sum
+$else
+PIXEL sum
+$endif
+
+include "../icombine.com"
+
+begin
+ # If no data has been excluded do the average/sum without checking
+ # the number of points and using the fact that the weights are
+ # normalized. If all the data has been excluded set the average/sum
+ # to the blank value if requested.
+
+ if (dflag == D_ALL) {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ k = i - 1
+ val = Mem$t[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ do j = 2, n[i] {
+ val = Mem$t[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val * wt) ** 2
+ }
+ average[i] = sqrt(sum)
+ }
+ } else {
+ do i = 1, npts {
+ k = i - 1
+ val = Mem$t[d[1]+k]
+ sum = val**2
+ do j = 2, n[i] {
+ val = Mem$t[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n[i]
+ else
+ average[i] = sqrt(sum)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ if (doblank == YES) {
+ do i = 1, npts
+ average[i] = blank
+ }
+ } else {
+ if (dowts && doaverage == YES) {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Mem$t[d[1]+k]
+ wt = wts[Memi[m[1]+k]]
+ sum = (val * wt) ** 2
+ sumwt = wt
+ do j = 2, n1 {
+ val = Mem$t[d[j]+k]
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (val* wt) ** 2
+ sumwt = sumwt + wt
+ }
+ if (doaverage == YES) {
+ if (sumwt > 0)
+ average[i] = sqrt(sum) / sumwt
+ else {
+ val = Mem$t[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Mem$t[d[j]+k]
+ sum = sum + val**2
+ }
+ average[i] = sqrt(sum) / n1
+ }
+ } else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = abs(n[i])
+ if (n1 > 0) {
+ k = i - 1
+ val = Mem$t[d[1]+k]
+ sum = val**2
+ do j = 2, n1 {
+ val = Mem$t[d[j]+k]
+ sum = sum + val**2
+ }
+ if (doaverage == YES)
+ average[i] = sqrt(sum) / n1
+ else
+ average[i] = sqrt(sum)
+ } else if (doblank == YES)
+ average[i] = blank
+ }
+ }
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icrmasks.x b/pkg/images/immatch/src/imcombine/src/icrmasks.x
new file mode 100644
index 00000000..8b9a0c3d
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icrmasks.x
@@ -0,0 +1,41 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+
+
+# IC_RMASKS -- Set pixels for rejection mask.
+
+procedure ic_rmasks (pm, v, id, nimages, n, npts)
+
+pointer pm #I Pixel mask
+long v[ARB] #I Output vector (input)
+pointer id[nimages] #I Image id pointers
+int nimages #I Number of images
+int n[npts] #I Number of good pixels
+int npts #I Number of output points per line
+
+int i, j, k, ndim, impnls()
+long v1[IM_MAXDIM]
+pointer buf
+
+begin
+ ndim = IM_NDIM(pm)
+ do k = 1, nimages {
+ call amovl (v, v1, ndim-1)
+ v1[ndim] = k
+ i = impnls (pm, buf, v1)
+ do j = 1, npts {
+ if (n[j] == nimages)
+ Mems[buf+j-1] = 0
+ else {
+ Mems[buf+j-1] = 1
+ do i = 1, n[j] {
+ if (Memi[id[i]+j-1] == k) {
+ Mems[buf+j-1] = 0
+ break
+ }
+ }
+ }
+ }
+ }
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icscale.x b/pkg/images/immatch/src/imcombine/src/icscale.x
new file mode 100644
index 00000000..42d62f8d
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icscale.x
@@ -0,0 +1,351 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include <imset.h>
+include "icombine.h"
+
+
+# IC_SCALE -- Get and set the scaling factors.
+#
+# If the scaling parameters have been set earlier then this routine
+# just normalizes the factors and writes the log output.
+# When dealing with individual images using image statistics for scaling
+# factors this routine determines the image statistics rather than being
+# done earlier since the input images have all been mapped at this stage.
+
+procedure ic_scale (in, out, offsets, scales, zeros, wts, nimages)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+int offsets[nimages,ARB] # Image offsets
+real scales[nimages] # Scale factors
+real zeros[nimages] # Zero or sky levels
+real wts[nimages] # Weights
+int nimages # Number of images
+
+int stype, ztype, wtype
+int i, j, k, l, nout
+real mode, median, mean, sumwts
+pointer sp, ncombine, exptime, modes, medians, means
+pointer section, str, sname, zname, wname, im, imref
+bool domode, domedian, domean, dozero, dos, doz, dow, snorm, znorm, wflag
+
+int imgeti(), strdic(), ic_gscale()
+real imgetr(), asumr(), asumi()
+pointer xt_opix()
+errchk ic_gscale, xt_opix, ic_statr
+
+include "icombine.com"
+
+begin
+ call smark (sp)
+ call salloc (ncombine, nimages, TY_INT)
+ call salloc (exptime, nimages, TY_REAL)
+ call salloc (modes, nimages, TY_REAL)
+ call salloc (medians, nimages, TY_REAL)
+ call salloc (means, nimages, TY_REAL)
+ call salloc (section, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (sname, SZ_FNAME, TY_CHAR)
+ call salloc (zname, SZ_FNAME, TY_CHAR)
+ call salloc (wname, SZ_FNAME, TY_CHAR)
+
+ # Get the number of images previously combined and the exposure times.
+ # The default combine number is 1 and the default exposure is 0.
+
+ do i = 1, nimages {
+ iferr (Memi[ncombine+i-1] = imgeti (in[i], "ncombine"))
+ Memi[ncombine+i-1] = 1
+ if (Memc[expkeyword] != EOS) {
+ iferr (Memr[exptime+i-1] = imgetr (in[i], Memc[expkeyword]))
+ Memr[exptime+i-1] = 0.
+ } else
+ Memr[exptime+i-1] = 0.
+ if (project) {
+ call amovki (Memi[ncombine], Memi[ncombine], nimages)
+ call amovkr (Memr[exptime], Memr[exptime], nimages)
+ break
+ }
+ }
+
+ # Set scaling type and factors.
+ stype = ic_gscale ("scale", Memc[sname], STYPES, in, Memr[exptime],
+ scales, nimages)
+ ztype = ic_gscale ("zero", Memc[zname], ZTYPES, in, Memr[exptime],
+ zeros, nimages)
+ wtype = ic_gscale ("weight", Memc[wname], WTYPES, in, Memr[exptime],
+ wts, nimages)
+
+ # Get image statistics if needed.
+ dos = ((stype==S_MODE)||(stype==S_MEDIAN)||(stype==S_MEAN))
+ doz = ((ztype==S_MODE)||(ztype==S_MEDIAN)||(ztype==S_MEAN))
+ dow = ((wtype==S_MODE)||(wtype==S_MEDIAN)||(wtype==S_MEAN))
+ if (dos) {
+ dos = false
+ do i = 1, nimages
+ if (IS_INDEFR(scales[i])) {
+ dos = true
+ break
+ }
+ }
+ if (doz) {
+ doz = false
+ do i = 1, nimages
+ if (IS_INDEFR(zeros[i])) {
+ doz = true
+ break
+ }
+ }
+ if (dow) {
+ dow = false
+ do i = 1, nimages
+ if (IS_INDEFR(wts[i])) {
+ dow = true
+ break
+ }
+ }
+
+ if (dos || doz || dow) {
+ domode = ((stype==S_MODE)||(ztype==S_MODE)||(wtype==S_MODE))
+ domedian = ((stype==S_MEDIAN)||(ztype==S_MEDIAN)||(wtype==S_MEDIAN))
+ domean = ((stype==S_MEAN)||(ztype==S_MEAN)||(wtype==S_MEAN))
+
+ Memc[section] = EOS
+ Memc[str] = EOS
+ call sscan (Memc[statsec])
+ call gargwrd (Memc[section], SZ_FNAME)
+ call gargwrd (Memc[str], SZ_LINE)
+
+ i = strdic (Memc[section], Memc[section], SZ_FNAME, S_SECTION)
+ switch (i) {
+ case S_INPUT:
+ call strcpy (Memc[str], Memc[section], SZ_FNAME)
+ imref = NULL
+ case S_OUTPUT:
+ call strcpy (Memc[str], Memc[section], SZ_FNAME)
+ imref = out[1]
+ case S_OVERLAP:
+ call strcpy ("[", Memc[section], SZ_FNAME)
+ do i = 1, IM_NDIM(out[1]) {
+ k = offsets[1,i] + 1
+ l = offsets[1,i] + IM_LEN(in[1],i)
+ do j = 2, nimages {
+ k = max (k, offsets[j,i]+1)
+ l = min (l, offsets[j,i]+IM_LEN(in[j],i))
+ }
+ if (i < IM_NDIM(out[1]))
+ call sprintf (Memc[str], SZ_LINE, "%d:%d,")
+ else
+ call sprintf (Memc[str], SZ_LINE, "%d:%d]")
+ call pargi (k)
+ call pargi (l)
+ call strcat (Memc[str], Memc[section], SZ_FNAME)
+ }
+ imref = out[1]
+ default:
+ imref = NULL
+ }
+
+ do i = 1, nimages {
+ im = xt_opix (in[i], i, 0)
+ if (imref != out[1])
+ imref = im
+ if ((dos && IS_INDEFR(scales[i])) ||
+ (doz && IS_INDEFR(zeros[i])) ||
+ (dow && IS_INDEFR(wts[i]))) {
+ call ic_statr (im, imref, Memc[section], offsets, i,
+ nimages, domode, domedian, domean, mode, median, mean)
+ if (domode) {
+ if (stype == S_MODE && IS_INDEFR(scales[i]))
+ scales[i] = mode
+ if (ztype == S_MODE && IS_INDEFR(zeros[i]))
+ zeros[i] = mode
+ if (wtype == S_MODE && IS_INDEFR(wts[i]))
+ wts[i] = mode
+ }
+ if (domedian) {
+ if (stype == S_MEDIAN && IS_INDEFR(scales[i]))
+ scales[i] = median
+ if (ztype == S_MEDIAN && IS_INDEFR(zeros[i]))
+ zeros[i] = median
+ if (wtype == S_MEDIAN && IS_INDEFR(wts[i]))
+ wts[i] = median
+ }
+ if (domean) {
+ if (stype == S_MEAN && IS_INDEFR(scales[i]))
+ scales[i] = mean
+ if (ztype == S_MEAN && IS_INDEFR(zeros[i]))
+ zeros[i] = mean
+ if (wtype == S_MEAN && IS_INDEFR(wts[i]))
+ wts[i] = mean
+ }
+ }
+ }
+ }
+
+ # Save the image statistics if computed.
+ call amovkr (INDEFR, Memr[modes], nimages)
+ call amovkr (INDEFR, Memr[medians], nimages)
+ call amovkr (INDEFR, Memr[means], nimages)
+ if (stype == S_MODE)
+ call amovr (scales, Memr[modes], nimages)
+ if (stype == S_MEDIAN)
+ call amovr (scales, Memr[medians], nimages)
+ if (stype == S_MEAN)
+ call amovr (scales, Memr[means], nimages)
+ if (ztype == S_MODE)
+ call amovr (zeros, Memr[modes], nimages)
+ if (ztype == S_MEDIAN)
+ call amovr (zeros, Memr[medians], nimages)
+ if (ztype == S_MEAN)
+ call amovr (zeros, Memr[means], nimages)
+ if (wtype == S_MODE)
+ call amovr (wts, Memr[modes], nimages)
+ if (wtype == S_MEDIAN)
+ call amovr (wts, Memr[medians], nimages)
+ if (wtype == S_MEAN)
+ call amovr (wts, Memr[means], nimages)
+
+ # If nothing else has set the scaling factors set them to defaults.
+ do i = 1, nimages {
+ if (IS_INDEFR(scales[i]))
+ scales[i] = 1.
+ if (IS_INDEFR(zeros[i]))
+ zeros[i] = 0.
+ if (IS_INDEFR(wts[i]))
+ wts[i] = 1.
+ }
+
+ do i = 1, nimages
+ if (scales[i] <= 0.) {
+ call eprintf ("WARNING: Negative scale factors")
+ call eprintf (" -- ignoring scaling\n")
+ call amovkr (1., scales, nimages)
+ break
+ }
+
+ # Convert to factors relative to the first image.
+ snorm = (stype == S_FILE || stype == S_KEYWORD)
+ znorm = (ztype == S_FILE || ztype == S_KEYWORD)
+ wflag = (wtype == S_FILE || wtype == S_KEYWORD)
+ if (snorm)
+ call arcpr (1., scales, scales, nimages)
+ mean = scales[1]
+ call adivkr (scales, mean, scales, nimages)
+ call adivr (zeros, scales, zeros, nimages)
+
+ if (wtype != S_NONE) {
+ do i = 1, nimages {
+ if (wts[i] < 0.) {
+ call eprintf ("WARNING: Negative weights")
+ call eprintf (" -- using only NCOMBINE weights\n")
+ do j = 1, nimages
+ wts[j] = Memi[ncombine+j-1]
+ break
+ }
+ if (ztype == S_NONE || znorm || wflag)
+ wts[i] = Memi[ncombine+i-1] * wts[i]
+ else {
+ if (zeros[i] <= 0.) {
+ call eprintf ("WARNING: Negative zero offsets")
+ call eprintf (" -- ignoring zero weight adjustments\n")
+ do j = 1, nimages
+ wts[j] = Memi[ncombine+j-1] * wts[j]
+ break
+ }
+ wts[i] = Memi[ncombine+i-1] * wts[i] * zeros[1] / zeros[i]
+ }
+ }
+ }
+
+ if (znorm)
+ call anegr (zeros, zeros, nimages)
+ else {
+ # Because of finite arithmetic it is possible for the zero offsets
+ # to be nonzero even when they are all equal. Just for the sake of
+ # a nice log set the zero offsets in this case.
+
+ mean = zeros[1]
+ call asubkr (zeros, mean, zeros, nimages)
+ for (i=2; (i<=nimages)&&(zeros[i]==zeros[1]); i=i+1)
+ ;
+ if (i > nimages)
+ call aclrr (zeros, nimages)
+ }
+ mean = asumr (wts, nimages)
+ if (mean > 0.)
+ call adivkr (wts, mean, wts, nimages)
+ else {
+ call eprintf ("WARNING: Mean weight is zero -- using no weights\n")
+ call amovkr (1., wts, nimages)
+ mean = 1.
+ }
+
+ # Set flags for scaling, zero offsets, sigma scaling, weights.
+ # Sigma scaling may be suppressed if the scales or zeros are
+ # different by a specified tolerance.
+
+ doscale = false
+ dozero = false
+ doscale1 = false
+ dowts = false
+ do i = 2, nimages {
+ if (snorm || scales[i] != scales[1])
+ doscale = true
+ if (znorm || zeros[i] != zeros[1])
+ dozero = true
+ if (wts[i] != wts[1])
+ dowts = true
+ }
+ if (doscale && sigscale != 0.) {
+ do i = 1, nimages {
+ if (abs (scales[i] - 1) > sigscale) {
+ doscale1 = true
+ break
+ }
+ }
+ }
+
+ # Set the output header parameters.
+ nout = asumi (Memi[ncombine], nimages)
+ call imaddi (out[1], "ncombine", nout)
+ mean = 0.
+ sumwts = 0.
+ do i = 1, nimages {
+ ifnoerr (mode = imgetr (in[i], "ccdmean")) {
+ mean = mean + wts[i] * mode / scales[i]
+ sumwts = sumwts + wts[i]
+ }
+ }
+ if (sumwts > 0.) {
+ mean = mean / sumwts
+ ifnoerr (mode = imgetr (out[1], "ccdmean")) {
+ call imaddr (out[1], "ccdmean", mean)
+ iferr (call imdelf (out[1], "ccdmeant"))
+ ;
+ }
+ }
+ if (out[2] != NULL) {
+ call imstats (out[2], IM_IMAGENAME, Memc[str], SZ_FNAME)
+ call imastr (out[1], "BPM", Memc[str])
+ }
+
+ # Start the log here since much of the info is only available here.
+ if (verbose) {
+ i = logfd
+ logfd = STDOUT
+ call ic_log (in, out, Memi[ncombine], Memr[exptime], Memc[sname],
+ Memc[zname], Memc[wname], Memr[modes], Memr[medians],
+ Memr[means], scales, zeros, wts, offsets, nimages, dozero,
+ nout)
+
+ logfd = i
+ }
+ call ic_log (in, out, Memi[ncombine], Memr[exptime], Memc[sname],
+ Memc[zname], Memc[wname], Memr[modes], Memr[medians], Memr[means],
+ scales, zeros, wts, offsets, nimages, dozero, nout)
+
+ doscale = (doscale || dozero)
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icsclip.gx b/pkg/images/immatch/src/imcombine/src/icsclip.gx
new file mode 100644
index 00000000..e4d8f027
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icsclip.gx
@@ -0,0 +1,504 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include "../icombine.h"
+
+define MINCLIP 3 # Mininum number of images for algorithm
+
+$for (sird)
+# IC_ASIGCLIP -- Reject pixels using sigma clipping about the average
+# The initial average rejects the high and low pixels. A correction for
+# different scalings of the images may be made. Weights are not used.
+
+procedure ic_asigclip$t (d, m, n, scales, zeros, nimages, npts, average)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+$if (datatype == sil)
+real average[npts] # Average
+$else
+PIXEL average[npts] # Average
+$endif
+
+int i, j, k, l, jj, n1, n2, nin, nk, maxkeep
+$if (datatype == sil)
+real d1, low, high, sum, a, s, r, one
+data one /1.0/
+$else
+PIXEL d1, low, high, sum, a, s, r, one
+data one /1$f/
+$endif
+pointer sp, resid, w, wp, dp1, dp2, mp1, mp2
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Flag whether returned average needs to be recomputed.
+ if (dowts || combine != AVERAGE)
+ docombine = true
+ else
+ docombine = false
+
+ # Save the residuals and the sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Do sigma clipping.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+
+ # If there are not enough pixels simply compute the average.
+ if (n1 < max (3, maxkeep)) {
+ if (!docombine) {
+ if (n1 == 0)
+ average[i] = blank
+ else {
+ sum = Mem$t[d[1]+k]
+ do j = 2, n1
+ sum = sum + Mem$t[d[j]+k]
+ average[i] = sum / n1
+ }
+ }
+ next
+ }
+
+ # Compute average with the high and low rejected.
+ low = Mem$t[d[1]+k]
+ high = Mem$t[d[2]+k]
+ if (low > high) {
+ d1 = low
+ low = high
+ high = d1
+ }
+ sum = 0.
+ do j = 3, n1 {
+ d1 = Mem$t[d[j]+k]
+ if (d1 < low) {
+ sum = sum + low
+ low = d1
+ } else if (d1 > high) {
+ sum = sum + high
+ high = d1
+ } else
+ sum = sum + d1
+ }
+ a = sum / (n1 - 2)
+ sum = sum + low + high
+
+ # Iteratively reject pixels and compute the final average if needed.
+ # Compact the data and keep track of the image IDs if needed.
+
+ repeat {
+ n2 = n1
+ if (doscale1) {
+ # Compute sigma corrected for scaling.
+ s = 0.
+ wp = w - 1
+ do j = 1, n1 {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Mem$t[dp1]
+ l = Memi[mp1]
+ r = sqrt (max (one, (a + zeros[l]) / scales[l]))
+ s = s + ((d1 - a) / r) ** 2
+ Memr[wp] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ wp = w - 1
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ wp = wp + 1
+
+ d1 = Mem$t[dp1]
+ r = (d1 - a) / (s * Memr[wp])
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ Memr[wp] = Memr[w+n1-1]
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ } else {
+ # Compute the sigma without scale correction.
+ s = 0.
+ do j = 1, n1
+ s = s + (Mem$t[d[j]+k] - a) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels. Save the residuals and data values.
+ if (s > 0.) {
+ for (j=1; j<=n1; j=j+1) {
+ dp1 = d[j] + k
+ d1 = Mem$t[dp1]
+ r = (d1 - a) / s
+ if (r < -lsigma || r > hsigma) {
+ Memr[resid+n1] = abs (r)
+ if (j < n1) {
+ dp2 = d[n1] + k
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[n1] + k
+ l = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = l
+ }
+ j = j - 1
+ }
+ sum = sum - d1
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ } until (n1 == n2 || n1 <= max (2, maxkeep))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ if (n1 < maxkeep) {
+ nk = maxkeep
+ if (doscale1) {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ mp1 = m[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mem$t[dp1]
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ sum = sum + Mem$t[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ } else {
+ for (j=n1+1; j<=nk; j=j+1) {
+ dp1 = d[j] + k
+ r = Memr[resid+j]
+ jj = 0
+ do l = j+1, n2 {
+ s = Memr[resid+l]
+ if (s < r + TOL) {
+ if (s > r - TOL)
+ jj = jj + 1
+ else {
+ jj = 0
+ Memr[resid+l] = r
+ r = s
+ dp2 = d[l] + k
+ d1 = Mem$t[dp1]
+ Mem$t[dp1] = Mem$t[dp2]
+ Mem$t[dp2] = d1
+ if (keepids) {
+ mp1 = m[j] + k
+ mp2 = m[l] + k
+ s = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = s
+ }
+ }
+ }
+ }
+ sum = sum + Mem$t[dp1]
+ n1 = n1 + 1
+ nk = max (nk, j+jj)
+ }
+ }
+
+ # Recompute the average.
+ if (n1 > 1)
+ a = sum / n1
+ }
+
+ # Save the average if needed.
+ n[i] = n1
+ if (!docombine) {
+ if (n1 > 0)
+ average[i] = a
+ else
+ average[i] = blank
+ }
+ }
+
+ # Check if the data flag has to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# IC_MSIGCLIP -- Reject pixels using sigma clipping about the median
+
+procedure ic_msigclip$t (d, m, n, scales, zeros, nimages, npts, median)
+
+pointer d[nimages] # Data pointers
+pointer m[nimages] # Image id pointers
+int n[npts] # Number of good pixels
+real scales[nimages] # Scales
+real zeros[nimages] # Zeros
+int nimages # Number of images
+int npts # Number of output points per line
+$if (datatype == sil)
+real median[npts] # Median
+$else
+PIXEL median[npts] # Median
+$endif
+
+int i, j, k, l, id, n1, n2, n3, nl, nh, nin, maxkeep
+real r, s
+pointer sp, resid, w, mp1, mp2
+$if (datatype == sil)
+real med, one
+data one /1.0/
+$else
+PIXEL med, one
+data one /1$f/
+$endif
+
+include "../icombine.com"
+
+begin
+ # If there are insufficient pixels go on to the combining
+ if (nkeep < 0)
+ maxkeep = max (0, nimages + nkeep)
+ else
+ maxkeep = min (nimages, nkeep)
+ if (nimages < max (MINCLIP, maxkeep+1) || dflag == D_NONE) {
+ docombine = true
+ return
+ }
+
+ # Save the residuals and sigma scaling corrections if needed.
+ call smark (sp)
+ call salloc (resid, nimages+1, TY_REAL)
+ if (doscale1)
+ call salloc (w, nimages, TY_REAL)
+
+ # Compute median and sigma and iteratively clip.
+ nin = max (0, n[1])
+ do i = 1, npts {
+ k = i - 1
+ n1 = max (0, n[i])
+ if (nkeep < 0)
+ maxkeep = max (0, n1 + nkeep)
+ else
+ maxkeep = min (n1, nkeep)
+ nl = 1
+ nh = n1
+
+ repeat {
+ n2 = n1
+ n3 = nl + n1 / 2
+
+ if (n1 == 0)
+ med = blank
+ else if (mod (n1, 2) == 0)
+ med = (Mem$t[d[n3-1]+k] + Mem$t[d[n3]+k]) / 2.
+ else
+ med = Mem$t[d[n3]+k]
+
+ if (n1 >= max (MINCLIP, maxkeep+1)) {
+ if (doscale1) {
+ # Compute the sigma with scaling correction.
+ s = 0.
+ do j = nl, nh {
+ l = Memi[m[j]+k]
+ r = sqrt (max (one, (med + zeros[l]) / scales[l]))
+ s = s + ((Mem$t[d[j]+k] - med) / r) ** 2
+ Memr[w+j-1] = r
+ }
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Mem$t[d[nl]+k]) / (s * Memr[w+nl-1])
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Mem$t[d[nh]+k] - med) / (s * Memr[w+nh-1])
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ } else {
+ # Compute the sigma without scaling correction.
+ s = 0.
+ do j = nl, nh
+ s = s + (Mem$t[d[j]+k] - med) ** 2
+ s = sqrt (s / (n1 - 1))
+
+ # Reject pixels and save the residuals.
+ if (s > 0.) {
+ for (; nl <= nh; nl = nl + 1) {
+ r = (med - Mem$t[d[nl]+k]) / s
+ if (r <= lsigma)
+ break
+ Memr[resid+nl] = r
+ n1 = n1 - 1
+ }
+ for (; nh >= nl; nh = nh - 1) {
+ r = (Mem$t[d[nh]+k] - med) / s
+ if (r <= hsigma)
+ break
+ Memr[resid+nh] = r
+ n1 = n1 - 1
+ }
+ }
+ }
+ }
+ } until (n1 == n2 || n1 < max (MINCLIP, maxkeep+1))
+
+ # If too many pixels are rejected add some back.
+ # All pixels with equal residuals are added back.
+ while (n1 < maxkeep) {
+ if (nl == 1)
+ nh = nh + 1
+ else if (nh == max (0, n[i]))
+ nl = nl - 1
+ else {
+ r = Memr[resid+nl-1]
+ s = Memr[resid+nh+1]
+ if (r < s) {
+ nl = nl - 1
+ r = r + TOL
+ if (s <= r)
+ nh = nh + 1
+ if (nl > 1) {
+ if (Memr[resid+nl-1] <= r)
+ nl = nl - 1
+ }
+ } else {
+ nh = nh + 1
+ s = s + TOL
+ if (r <= s)
+ nl = nl - 1
+ if (nh < n2) {
+ if (Memr[resid+nh+1] <= s)
+ nh = nh + 1
+ }
+ }
+ }
+ n1 = nh - nl + 1
+ }
+
+ # Only set median and reorder if needed
+ n[i] = n1
+ if (n1 > 0 && nl > 1 && (combine != MEDIAN || grow >= 1.)) {
+ j = max (nl, n1 + 1)
+ if (keepids) {
+ do l = 1, min (n1, nl-1) {
+ Mem$t[d[l]+k] = Mem$t[d[j]+k]
+ if (grow >= 1.) {
+ mp1 = m[l] + k
+ mp2 = m[j] + k
+ id = Memi[mp1]
+ Memi[mp1] = Memi[mp2]
+ Memi[mp2] = id
+ } else
+ Memi[m[l]+k] = Memi[m[j]+k]
+ j = j + 1
+ }
+ } else {
+ do l = 1, min (n1, nl - 1) {
+ Mem$t[d[l]+k] = Mem$t[d[j]+k]
+ j = j + 1
+ }
+ }
+ }
+
+ if (combine == MEDIAN)
+ median[i] = med
+ }
+
+ # Check if data flag needs to be reset for rejected pixels
+ if (dflag == D_ALL) {
+ do i = 1, npts {
+ if (max (0, n[i]) != nin) {
+ dflag = D_MIX
+ break
+ }
+ }
+ }
+
+ # Flag that the median has been computed.
+ if (combine == MEDIAN)
+ docombine = false
+ else
+ docombine = true
+
+ call sfree (sp)
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icsection.x b/pkg/images/immatch/src/imcombine/src/icsection.x
new file mode 100644
index 00000000..746c1f51
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icsection.x
@@ -0,0 +1,94 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <ctype.h>
+
+# IC_SECTION -- Parse an image section into its elements.
+# 1. The default values must be set by the caller.
+# 2. A null image section is OK.
+# 3. The first nonwhitespace character must be '['.
+# 4. The last interpreted character must be ']'.
+#
+# This procedure should be replaced with an IMIO procedure at some
+# point.
+
+procedure ic_section (section, x1, x2, xs, ndim)
+
+char section[ARB] # Image section
+int x1[ndim] # Starting pixel
+int x2[ndim] # Ending pixel
+int xs[ndim] # Step
+int ndim # Number of dimensions
+
+int i, ip, a, b, c, temp, ctoi()
+define error_ 99
+
+begin
+ # Decode the section string.
+ ip = 1
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+ if (section[ip] == '[')
+ ip = ip + 1
+ else if (section[ip] == EOS)
+ return
+ else
+ goto error_
+
+ do i = 1, ndim {
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+ if (section[ip] == ']')
+ break
+
+ # Default values
+ a = x1[i]
+ b = x2[i]
+ c = xs[i]
+
+ # Get a:b:c. Allow notation such as "-*:c"
+ # (or even "-:c") where the step is obviously negative.
+
+ if (ctoi (section, ip, temp) > 0) { # a
+ a = temp
+ if (section[ip] == ':') {
+ ip = ip + 1
+ if (ctoi (section, ip, b) == 0) # a:b
+ goto error_
+ } else
+ b = a
+ } else if (section[ip] == '-') { # -*
+ temp = a
+ a = b
+ b = temp
+ ip = ip + 1
+ if (section[ip] == '*')
+ ip = ip + 1
+ } else if (section[ip] == '*') # *
+ ip = ip + 1
+ if (section[ip] == ':') { # ..:step
+ ip = ip + 1
+ if (ctoi (section, ip, c) == 0)
+ goto error_
+ else if (c == 0)
+ goto error_
+ }
+ if (a > b && c > 0)
+ c = -c
+
+ x1[i] = a
+ x2[i] = b
+ xs[i] = c
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+ if (section[ip] == ',')
+ ip = ip + 1
+ }
+
+ if (section[ip] != ']')
+ goto error_
+
+ return
+error_
+ call error (0, "Error in image section specification")
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icsetout.x b/pkg/images/immatch/src/imcombine/src/icsetout.x
new file mode 100644
index 00000000..efe55681
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icsetout.x
@@ -0,0 +1,332 @@
+include <imhdr.h>
+include <imset.h>
+include <mwset.h>
+
+define OFFTYPES "|none|wcs|world|physical|grid|"
+define FILE 0
+define NONE 1
+define WCS 2
+define WORLD 3
+define PHYSICAL 4
+define GRID 5
+
+# IC_SETOUT -- Set output image size and offsets of input images.
+
+procedure ic_setout (in, out, offsets, nimages)
+
+pointer in[nimages] # Input images
+pointer out[ARB] # Output images
+int offsets[nimages,ARB] # Offsets
+int nimages # Number of images
+
+int i, j, indim, outdim, mwdim, a, b, amin, bmax, fd, offtype, npix
+real val
+bool proj, reloff, flip, streq(), fp_equald()
+pointer sp, str, fname
+pointer ltv, lref, wref, cd, ltm, coord, shift, axno, axval, section
+pointer mw, ct, mw_openim(), mw_sctran(), xt_immap()
+int open(), fscan(), nscan(), mw_stati(), strlen(), strdic()
+errchk mw_openim, mw_gwtermd, mw_gltermd, mw_gaxmap
+errchk mw_sctran, mw_ctrand, open, xt_immap
+
+include "icombine.com"
+define newscan_ 10
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (ltv, IM_MAXDIM, TY_DOUBLE)
+ call salloc (ltm, IM_MAXDIM*IM_MAXDIM, TY_DOUBLE)
+ call salloc (lref, IM_MAXDIM, TY_DOUBLE)
+ call salloc (wref, IM_MAXDIM, TY_DOUBLE)
+ call salloc (cd, IM_MAXDIM*IM_MAXDIM, TY_DOUBLE)
+ call salloc (coord, IM_MAXDIM, TY_DOUBLE)
+ call salloc (shift, IM_MAXDIM, TY_REAL)
+ call salloc (axno, IM_MAXDIM, TY_INT)
+ call salloc (axval, IM_MAXDIM, TY_INT)
+
+ # Check and set the image dimensionality.
+ indim = IM_NDIM(in[1])
+ outdim = IM_NDIM(out[1])
+ proj = (indim != outdim)
+ if (!proj) {
+ do i = 1, nimages
+ if (IM_NDIM(in[i]) != outdim) {
+ call sfree (sp)
+ call error (1, "Image dimensions are not the same")
+ }
+ }
+
+ # Set the reference point to that of the first image.
+ mw = mw_openim (in[1])
+ call mw_seti (mw, MW_USEAXMAP, NO)
+ mwdim = mw_stati (mw, MW_NPHYSDIM)
+ call mw_gwtermd (mw, Memd[lref], Memd[wref], Memd[cd], mwdim)
+ ct = mw_sctran (mw, "world", "logical", 0)
+ call mw_ctrand (ct, Memd[wref], Memd[lref], mwdim)
+ call mw_ctfree (ct)
+ if (proj)
+ Memd[lref+outdim] = 1
+
+ # Parse the user offset string. If "none" then there are no offsets.
+ # If "world" or "wcs" then set the offsets based on the world WCS.
+ # If "physical" then set the offsets based on the physical WCS.
+ # If "grid" then set the offsets based on the input grid parameters.
+ # If a file scan it.
+
+ call clgstr ("offsets", Memc[fname], SZ_FNAME)
+ call sscan (Memc[fname])
+ call gargwrd (Memc[fname], SZ_FNAME)
+ if (nscan() == 0)
+ offtype = NONE
+ else {
+ offtype = strdic (Memc[fname], Memc[str], SZ_FNAME, OFFTYPES)
+ if (offtype > 0 && !streq (Memc[fname], Memc[str]))
+ offtype = 0
+ }
+ if (offtype == 0)
+ offtype = FILE
+
+ switch (offtype) {
+ case NONE:
+ call aclri (offsets, outdim*nimages)
+ reloff = true
+ case WORLD, WCS:
+ do j = 1, outdim
+ offsets[1,j] = 0
+ if (proj) {
+ ct = mw_sctran (mw, "world", "logical", 0)
+ do i = 2, nimages {
+ Memd[wref+outdim] = i
+ call mw_ctrand (ct, Memd[wref], Memd[coord], indim)
+ do j = 1, outdim
+ offsets[i,j] = nint (Memd[lref+j-1] - Memd[coord+j-1])
+ }
+ call mw_ctfree (ct)
+ call mw_close (mw)
+ } else {
+ ct = mw_sctran (mw, "world", "logical", 0)
+ call mw_ctrand (ct, Memd[wref], Memd[lref], indim)
+ do i = 2, nimages {
+ call mw_close (mw)
+ mw = mw_openim (in[i])
+ ct = mw_sctran (mw, "world", "logical", 0)
+ call mw_ctrand (ct, Memd[wref], Memd[coord], indim)
+ do j = 1, outdim
+ offsets[i,j] = nint (Memd[lref+j-1] - Memd[coord+j-1])
+ call mw_ctfree (ct)
+ }
+ }
+ reloff = true
+ case PHYSICAL:
+ call salloc (section, SZ_FNAME, TY_CHAR)
+
+ call mw_gltermd (mw, Memd[ltm], Memd[coord], indim)
+ do i = 2, nimages {
+ call mw_close (mw)
+ mw = mw_openim (in[i])
+ call mw_gltermd (mw, Memd[cd], Memd[coord], indim)
+ call strcpy ("[", Memc[section], SZ_FNAME)
+ flip = false
+ do j = 0, indim*indim-1, indim+1 {
+ if (Memd[ltm+j] * Memd[cd+j] >= 0.)
+ call strcat ("*,", Memc[section], SZ_FNAME)
+ else {
+ call strcat ("-*,", Memc[section], SZ_FNAME)
+ flip = true
+ }
+ }
+ Memc[section+strlen(Memc[section])-1] = ']'
+ if (flip) {
+ call imstats (in[i], IM_IMAGENAME, Memc[fname], SZ_FNAME)
+ call strcat (Memc[section], Memc[fname], SZ_FNAME)
+ call xt_imunmap (in[i], i)
+ in[i] = xt_immap (Memc[fname], READ_ONLY, TY_CHAR, i, 0)
+ call mw_close (mw)
+ mw = mw_openim (in[i])
+ call mw_gltermd (mw, Memd[cd], Memd[coord], indim)
+ do j = 0, indim*indim-1
+ if (!fp_equald (Memd[ltm+j], Memd[cd+j]))
+ call error (1,
+ "Cannot match physical coordinates")
+ }
+ }
+
+ call mw_close (mw)
+ mw = mw_openim (in[1])
+ ct = mw_sctran (mw, "logical", "physical", 0)
+ call mw_ctrand (ct, Memd[lref], Memd[ltv], indim)
+ call mw_ctfree (ct)
+ do j = 1, outdim
+ offsets[1,j] = 0
+ if (proj) {
+ ct = mw_sctran (mw, "physical", "logical", 0)
+ do i = 2, nimages {
+ Memd[ltv+outdim] = i
+ call mw_ctrand (ct, Memd[ltv], Memd[coord], indim)
+ do j = 1, outdim
+ offsets[i,j] = nint (Memd[lref+j-1] - Memd[coord+j-1])
+ }
+ call mw_ctfree (ct)
+ call mw_close (mw)
+ } else {
+ do i = 2, nimages {
+ call mw_close (mw)
+ mw = mw_openim (in[i])
+ ct = mw_sctran (mw, "physical", "logical", 0)
+ call mw_ctrand (ct, Memd[ltv], Memd[coord], indim)
+ do j = 1, outdim
+ offsets[i,j] = nint (Memd[lref+j-1] - Memd[coord+j-1])
+ call mw_ctfree (ct)
+ }
+ }
+ reloff = true
+ case GRID:
+ amin = 1
+ do j = 1, outdim {
+ call gargi (a)
+ call gargi (b)
+ if (nscan() < 1+2*j) {
+ a = 1
+ b = 0
+ }
+ do i = 1, nimages
+ offsets[i,j] = mod ((i-1)/amin, a) * b
+ amin = amin * a
+ }
+ reloff = true
+ case FILE:
+ reloff = true
+ fd = open (Memc[fname], READ_ONLY, TEXT_FILE)
+ do i = 1, nimages {
+newscan_ if (fscan (fd) == EOF)
+ call error (1, "IMCOMBINE: Offset list too short")
+ call gargwrd (Memc[fname], SZ_FNAME)
+ if (Memc[fname] == '#') {
+ call gargwrd (Memc[fname], SZ_FNAME)
+ call strlwr (Memc[fname])
+ if (streq (Memc[fname], "absolute"))
+ reloff = false
+ else if (streq (Memc[fname], "relative"))
+ reloff = true
+ goto newscan_
+ }
+ call reset_scan ()
+ do j = 1, outdim {
+ call gargr (val)
+ offsets[i,j] = nint (val)
+ }
+ if (nscan() < outdim)
+ call error (1, "IMCOMBINE: Error in offset list")
+ }
+ call close (fd)
+ }
+
+ # Set the output image size and the aligned flag
+ aligned = true
+ do j = 1, outdim {
+ a = offsets[1,j]
+ b = IM_LEN(in[1],j) + a
+ amin = a
+ bmax = b
+ do i = 2, nimages {
+ a = offsets[i,j]
+ b = IM_LEN(in[i],j) + a
+ if (a != amin || b != bmax || !reloff)
+ aligned = false
+ amin = min (a, amin)
+ bmax = max (b, bmax)
+ }
+ IM_LEN(out[1],j) = bmax
+ if (reloff || amin < 0) {
+ do i = 1, nimages
+ offsets[i,j] = offsets[i,j] - amin
+ IM_LEN(out[1],j) = IM_LEN(out[1],j) - amin
+ }
+ }
+
+ # Get the output limits.
+ call clgstr ("outlimits", Memc[fname], SZ_FNAME)
+ call sscan (Memc[fname])
+ do j = 1, outdim {
+ call gargi (a)
+ call gargi (b)
+ if (nscan() < 2*j)
+ break
+ if (!IS_INDEFI(a)) {
+ do i = 1, nimages {
+ offsets[i,j] = offsets[i,j] - a + 1
+ if (offsets[i,j] != 0)
+ aligned = false
+ }
+ IM_LEN(out[1],j) = IM_LEN(out[1],j) - a + 1
+ }
+ if (!IS_INDEFI(a) && !IS_INDEFI(b))
+ IM_LEN(out[1],j) = min (IM_LEN(out[1],j), b - a + 1)
+ }
+
+ # Update the WCS.
+ if (proj || !aligned || !reloff) {
+ call mw_close (mw)
+ mw = mw_openim (out[1])
+ mwdim = mw_stati (mw, MW_NPHYSDIM)
+ call mw_gaxmap (mw, Memi[axno], Memi[axval], mwdim)
+ if (!aligned || !reloff) {
+ call mw_gltermd (mw, Memd[cd], Memd[lref], mwdim)
+ do i = 1, mwdim {
+ j = Memi[axno+i-1]
+ if (j > 0 && j <= indim)
+ Memd[lref+i-1] = Memd[lref+i-1] + offsets[1,j]
+ }
+ if (proj)
+ Memd[lref+mwdim-1] = 0.
+ call mw_sltermd (mw, Memd[cd], Memd[lref], mwdim)
+ }
+ if (proj) {
+ # Apply dimensional reduction.
+ do i = 1, mwdim {
+ j = Memi[axno+i-1]
+ if (j <= outdim)
+ next
+ else if (j > outdim+1)
+ Memi[axno+i-1] = j - 1
+ else {
+ Memi[axno+i-1] = 0
+ Memi[axval+i-1] = 0
+ }
+ }
+ call mw_saxmap (mw, Memi[axno], Memi[axval], mwdim)
+ }
+
+ # Reset physical coordinates.
+ if (offtype == WCS || offtype == WORLD) {
+ call mw_gltermd (mw, Memd[ltm], Memd[ltv], mwdim)
+ call mw_gwtermd (mw, Memd[lref], Memd[wref], Memd[cd], mwdim)
+ call mwvmuld (Memd[ltm], Memd[lref], Memd[lref], mwdim)
+ call aaddd (Memd[lref], Memd[ltv], Memd[lref], mwdim)
+ call mwinvertd (Memd[ltm], Memd[ltm], mwdim)
+ call mwmmuld (Memd[cd], Memd[ltm], Memd[cd], mwdim)
+ call mw_swtermd (mw, Memd[lref], Memd[wref], Memd[cd], mwdim)
+ call aclrd (Memd[ltv], mwdim)
+ call aclrd (Memd[ltm], mwdim*mwdim)
+ do i = 1, mwdim
+ Memd[ltm+(i-1)*(mwdim+1)] = 1.
+ call mw_sltermd (mw, Memd[ltm], Memd[ltv], mwdim)
+ }
+ call mw_saveim (mw, out)
+ }
+ call mw_close (mw)
+
+ # Throw an error if the output size is too large.
+ if (offtype != NONE) {
+ npix = IM_LEN(out[1],1)
+ do i = 2, outdim
+ npix = npix * IM_LEN(out[1],i)
+ npix = npix / 1000000000
+ if (npix > 100)
+ call error (1, "Output has more than 100 Gpixels (check offsets)")
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/icsigma.gx b/pkg/images/immatch/src/imcombine/src/icsigma.gx
new file mode 100644
index 00000000..1304d940
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icsigma.gx
@@ -0,0 +1,122 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include "../icombine.h"
+
+$for (sird)
+# IC_SIGMA -- Compute the sigma image line.
+# The estimated sigma includes a correction for the finite population.
+# Weights are used if desired.
+
+procedure ic_sigma$t (d, m, n, wts, npts, average, sigma)
+
+pointer d[ARB] # Data pointers
+pointer m[ARB] # Image ID pointers
+int n[npts] # Number of points
+real wts[ARB] # Weights
+int npts # Number of output points per line
+$if (datatype == sil)
+real average[npts] # Average
+real sigma[npts] # Sigma line (returned)
+$else
+PIXEL average[npts] # Average
+PIXEL sigma[npts] # Sigma line (returned)
+$endif
+
+int i, j, k, n1
+real wt, sigcor, sumwt
+$if (datatype == sil)
+real a, sum
+$else
+PIXEL a, sum
+$endif
+
+include "../icombine.com"
+
+begin
+ if (dflag == D_ALL) {
+ n1 = n[1]
+ if (dowts) {
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Mem$t[d[1]+k] - a) ** 2 * wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Mem$t[d[j]+k] - a) ** 2 * wt
+ }
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ } else {
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ do i = 1, npts {
+ k = i - 1
+ a = average[i]
+ sum = (Mem$t[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Mem$t[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ }
+ }
+ } else if (dflag == D_NONE) {
+ do i = 1, npts
+ sigma[i] = blank
+ } else {
+ if (dowts) {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = real (n1) / real (n1 -1)
+ else
+ sigcor = 1
+ a = average[i]
+ wt = wts[Memi[m[1]+k]]
+ sum = (Mem$t[d[1]+k] - a) ** 2 * wt
+ sumwt = wt
+ do j = 2, n1 {
+ wt = wts[Memi[m[j]+k]]
+ sum = sum + (Mem$t[d[j]+k] - a) ** 2 * wt
+ sumwt = sumwt + wt
+ }
+ if (sumwt > 0)
+ sigma[i] = sqrt (sum / sumwt * sigcor)
+ else {
+ sum = (Mem$t[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Mem$t[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum / n1 * sigcor)
+ }
+ } else
+ sigma[i] = blank
+ }
+ } else {
+ do i = 1, npts {
+ n1 = n[i]
+ if (n1 > 0) {
+ k = i - 1
+ if (n1 > 1)
+ sigcor = 1. / real (n1 - 1)
+ else
+ sigcor = 1.
+ a = average[i]
+ sum = (Mem$t[d[1]+k] - a) ** 2
+ do j = 2, n1
+ sum = sum + (Mem$t[d[j]+k] - a) ** 2
+ sigma[i] = sqrt (sum * sigcor)
+ } else
+ sigma[i] = blank
+ }
+ }
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icsort.gx b/pkg/images/immatch/src/imcombine/src/icsort.gx
new file mode 100644
index 00000000..e124da15
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icsort.gx
@@ -0,0 +1,386 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+define LOGPTR 32 # log2(maxpts) (4e9)
+
+$for (sird)
+# IC_SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially.
+
+procedure ic_sort$t (a, b, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+PIXEL b[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+PIXEL pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR]
+define swap {temp=$1;$1=$2;$2=temp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix
+ b[i] = Mem$t[a[i]+l]
+
+ # Special cases
+ $if (datatype == x)
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (abs (temp) < abs (pivot)) {
+ b[1] = temp
+ b[2] = pivot
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (abs (temp) < abs (pivot)) { # bac|bca|cba
+ if (abs (temp) < abs (temp3)) { # bac|bca
+ b[1] = temp
+ if (abs (pivot) < abs (temp3)) # bac
+ b[2] = pivot
+ else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ }
+ } else if (abs (temp3) < abs (temp)) { # acb|cab
+ b[3] = temp
+ if (abs (pivot) < abs (temp3)) # acb
+ b[2] = temp3
+ else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+ $else
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) # bac
+ b[2] = pivot
+ else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) # acb
+ b[2] = temp3
+ else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+ $endif
+
+ # General case
+ do i = 1, npix
+ b[i] = Mem$t[a[i]+l]
+
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ $if (datatype == x)
+ for (i=i+1; abs(b[i]) < abs(pivot); i=i+1)
+ $else
+ for (i=i+1; b[i] < pivot; i=i+1)
+ $endif
+ ;
+ for (j=j-1; j > i; j=j-1)
+ $if (datatype == x)
+ if (abs(b[j]) <= abs(pivot))
+ $else
+ if (b[j] <= pivot)
+ $endif
+ break
+ if (i < j) # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix
+ Mem$t[a[i]+l] = b[i]
+ }
+end
+
+
+# IC_2SORT -- Quicksort. This is based on the VOPS asrt except that
+# the input is an array of pointers to image lines and the sort is done
+# across the image lines at each point along the lines. The number of
+# valid pixels at each point is allowed to vary. The cases of 1, 2, and 3
+# pixels per point are treated specially. A second integer set of
+# vectors is sorted.
+
+procedure ic_2sort$t (a, b, c, d, nvecs, npts)
+
+pointer a[ARB] # pointer to input vectors
+PIXEL b[ARB] # work array
+pointer c[ARB] # pointer to associated integer vectors
+int d[ARB] # work array
+int nvecs[npts] # number of vectors
+int npts # number of points in vectors
+
+PIXEL pivot, temp, temp3
+int i, j, k, l, p, npix, lv[LOGPTR], uv[LOGPTR], itemp
+define swap {temp=$1;$1=$2;$2=temp}
+define iswap {itemp=$1;$1=$2;$2=itemp}
+define copy_ 10
+
+begin
+ do l = 0, npts-1 {
+ npix = nvecs[l+1]
+ if (npix <= 1)
+ next
+
+ do i = 1, npix {
+ b[i] = Mem$t[a[i]+l]
+ d[i] = Memi[c[i]+l]
+ }
+
+ # Special cases
+ $if (datatype == x)
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (abs (temp) < abs (pivot)) {
+ b[1] = temp
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (abs (temp) < abs (pivot)) { # bac|bca|cba
+ if (abs (temp) < abs (temp3)) { # bac|bca
+ b[1] = temp
+ if (abs (pivot) < abs (temp3)) { # bac
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ itemp = d[2]
+ d[2] = d[3]
+ d[3] = d[1]
+ d[1] = itemp
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ iswap (d[1], d[3])
+ }
+ } else if (abs (temp3) < abs (temp)) { # acb|cab
+ b[3] = temp
+ if (abs (pivot) < abs (temp3)) { # acb
+ b[2] = temp3
+ iswap (d[2], d[3])
+ } else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ itemp = d[2]
+ d[2] = d[1]
+ d[1] = d[3]
+ d[3] = itemp
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+ $else
+ if (npix <= 3) {
+ pivot = b[1]
+ temp = b[2]
+ if (npix == 2) {
+ if (temp < pivot) {
+ b[1] = temp
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else
+ next
+ } else {
+ temp3 = b[3]
+ if (temp < pivot) { # bac|bca|cba
+ if (temp < temp3) { # bac|bca
+ b[1] = temp
+ if (pivot < temp3) { # bac
+ b[2] = pivot
+ iswap (d[1], d[2])
+ } else { # bca
+ b[2] = temp3
+ b[3] = pivot
+ itemp = d[2]
+ d[2] = d[3]
+ d[3] = d[1]
+ d[1] = itemp
+ }
+ } else { # cba
+ b[1] = temp3
+ b[3] = pivot
+ iswap (d[1], d[3])
+ }
+ } else if (temp3 < temp) { # acb|cab
+ b[3] = temp
+ if (pivot < temp3) { # acb
+ b[2] = temp3
+ iswap (d[2], d[3])
+ } else { # cab
+ b[1] = temp3
+ b[2] = pivot
+ itemp = d[2]
+ d[2] = d[1]
+ d[1] = d[3]
+ d[3] = itemp
+ }
+ } else
+ next
+ }
+ goto copy_
+ }
+ $endif
+
+ # General case
+ lv[1] = 1
+ uv[1] = npix
+ p = 1
+
+ while (p > 0) {
+ if (lv[p] >= uv[p]) # only one elem in this subset
+ p = p - 1 # pop stack
+ else {
+ # Dummy do loop to trigger the Fortran optimizer.
+ do p = p, ARB {
+ i = lv[p] - 1
+ j = uv[p]
+
+ # Select as the pivot the element at the center of the
+ # array, to avoid quadratic behavior on an already
+ # sorted array.
+
+ k = (lv[p] + uv[p]) / 2
+ swap (b[j], b[k]); swap (d[j], d[k])
+ pivot = b[j] # pivot line
+
+ while (i < j) {
+ $if (datatype == x)
+ for (i=i+1; abs(b[i]) < abs(pivot); i=i+1)
+ $else
+ for (i=i+1; b[i] < pivot; i=i+1)
+ $endif
+ ;
+ for (j=j-1; j > i; j=j-1)
+ $if (datatype == x)
+ if (abs(b[j]) <= abs(pivot))
+ $else
+ if (b[j] <= pivot)
+ $endif
+ break
+ if (i < j) { # out of order pair
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+ }
+ }
+
+ j = uv[p] # move pivot to position i
+ swap (b[i], b[j]) # interchange elements
+ swap (d[i], d[j])
+
+ if (i-lv[p] < uv[p] - i) { # stack so shorter done first
+ lv[p+1] = lv[p]
+ uv[p+1] = i - 1
+ lv[p] = i + 1
+ } else {
+ lv[p+1] = i + 1
+ uv[p+1] = uv[p]
+ uv[p] = i - 1
+ }
+
+ break
+ }
+ p = p + 1 # push onto stack
+ }
+ }
+
+copy_
+ do i = 1, npix {
+ Mem$t[a[i]+l] = b[i]
+ Memi[c[i]+l] = d[i]
+ }
+ }
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/icstat.gx b/pkg/images/immatch/src/imcombine/src/icstat.gx
new file mode 100644
index 00000000..c594182b
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/icstat.gx
@@ -0,0 +1,238 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <imhdr.h>
+include "../icombine.h"
+
+define NMAX 100000 # Maximum number of pixels to sample
+
+$for (sird)
+# IC_STAT -- Compute image statistics within specified section.
+# The image section is relative to a reference image which may be
+# different than the input image and may have an offset. Only a
+# subsample of pixels is used. Masked and thresholded pixels are
+# ignored. Only the desired statistics are computed to increase
+# efficiency.
+
+procedure ic_stat$t (im, imref, section, offsets, image, nimages,
+ domode, domedian, domean, mode, median, mean)
+
+pointer im # Data image
+pointer imref # Reference image for image section
+char section[ARB] # Image section
+int offsets[nimages,ARB] # Image section offset from data to reference
+int image # Image index (for mask I/O)
+int nimages # Number of images in offsets.
+bool domode, domedian, domean # Statistics to compute
+real mode, median, mean # Statistics
+
+int i, j, ndim, n, nv
+real a
+pointer sp, v1, v2, dv, va, vb
+pointer data, mask, dp, lp, mp, imgnl$t()
+
+$if (datatype == csir)
+real asum$t()
+$else $if (datatype == ld)
+double asum$t()
+$else
+PIXEL asum$t()
+$endif $endif
+PIXEL ic_mode$t()
+
+include "../icombine.com"
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+ call salloc (dv, IM_MAXDIM, TY_LONG)
+ call salloc (va, IM_MAXDIM, TY_LONG)
+ call salloc (vb, IM_MAXDIM, TY_LONG)
+
+ # Determine the image section parameters. This must be in terms of
+ # the data image pixel coordinates though the section may be specified
+ # in terms of the reference image coordinates. Limit the number of
+ # pixels in each dimension to a maximum.
+
+ ndim = IM_NDIM(im)
+ if (project)
+ ndim = ndim - 1
+ call amovki (1, Memi[v1], IM_MAXDIM)
+ call amovki (1, Memi[va], IM_MAXDIM)
+ call amovki (1, Memi[dv], IM_MAXDIM)
+ call amovi (IM_LEN(imref,1), Memi[vb], ndim)
+ call ic_section (section, Memi[va], Memi[vb], Memi[dv], ndim)
+ if (im != imref)
+ do i = 1, ndim {
+ Memi[va+i-1] = Memi[va+i-1] - offsets[image,i]
+ Memi[vb+i-1] = Memi[vb+i-1] - offsets[image,i]
+ }
+
+ do j = 1, 10 {
+ n = 1
+ do i = 0, ndim-1 {
+ Memi[v1+i] = max (1, min (Memi[va+i], Memi[vb+i]))
+ Memi[v2+i] = min (IM_LEN(im,i+1), max (Memi[va+i], Memi[vb+i]))
+ Memi[dv+i] = j
+ nv = max (1, (Memi[v2+i] - Memi[v1+i]) / Memi[dv+i] + 1)
+ Memi[v2+i] = Memi[v1+i] + (nv - 1) * Memi[dv+i]
+ n = n * nv
+ }
+ if (n < NMAX)
+ break
+ }
+
+ call amovl (Memi[v1], Memi[va], IM_MAXDIM)
+ Memi[va] = 1
+ if (project)
+ Memi[va+ndim] = image
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+
+ # Accumulate the pixel values within the section. Masked pixels and
+ # thresholded pixels are ignored.
+
+ call salloc (data, n, TY_PIXEL)
+ dp = data
+ while (imgnl$t (im, lp, Memi[vb]) != EOF) {
+ call ic_mget1 (im, image, nimages, offsets[image,1], Memi[va], mask)
+ lp = lp + Memi[v1] - 1
+ if (dflag == D_ALL) {
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ a = Mem$t[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Mem$t[dp] = a
+ dp = dp + 1
+ }
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ Mem$t[dp] = Mem$t[lp]
+ dp = dp + 1
+ lp = lp + Memi[dv]
+ }
+ }
+ } else if (dflag == D_MIX) {
+ mp = mask + Memi[v1] - 1
+ if (dothresh) {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ a = Mem$t[lp]
+ if (a >= lthresh && a <= hthresh) {
+ Mem$t[dp] = a
+ dp = dp + 1
+ }
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ } else {
+ do i = Memi[v1], Memi[v2], Memi[dv] {
+ if (Memi[mp] == 0) {
+ Mem$t[dp] = Mem$t[lp]
+ dp = dp + 1
+ }
+ mp = mp + Memi[dv]
+ lp = lp + Memi[dv]
+ }
+ }
+ }
+ for (i=2; i<=ndim; i=i+1) {
+ Memi[va+i-1] = Memi[va+i-1] + Memi[dv+i-1]
+ if (Memi[va+i-1] <= Memi[v2+i-1])
+ break
+ Memi[va+i-1] = Memi[v1+i-1]
+ }
+ if (i > ndim)
+ break
+ call amovl (Memi[va], Memi[vb], IM_MAXDIM)
+ }
+
+ # Close mask until it is needed again.
+ call ic_mclose1 (image, nimages)
+
+ n = dp - data
+ if (n < 1) {
+ call sfree (sp)
+ call error (1, "Image section contains no pixels")
+ }
+
+ # Compute only statistics needed.
+ if (domode || domedian) {
+ call asrt$t (Mem$t[data], Mem$t[data], n)
+ mode = ic_mode$t (Mem$t[data], n)
+ median = Mem$t[data+n/2-1]
+ }
+ if (domean)
+ mean = asum$t (Mem$t[data], n) / n
+
+ call sfree (sp)
+end
+
+
+define NMIN 10 # Minimum number of pixels for mode calculation
+define ZRANGE 0.7 # Fraction of pixels about median to use
+define ZSTEP 0.01 # Step size for search for mode
+define ZBIN 0.1 # Bin size for mode.
+
+# IC_MODE -- Compute mode of an array. The mode is found by binning
+# with a bin size based on the data range over a fraction of the
+# pixels about the median and a bin step which may be smaller than the
+# bin size. If there are too few points the median is returned.
+# The input array must be sorted.
+
+PIXEL procedure ic_mode$t (a, n)
+
+PIXEL a[n] # Data array
+int n # Number of points
+
+int i, j, k, nmax
+real z1, z2, zstep, zbin
+PIXEL mode
+bool fp_equalr()
+
+begin
+ if (n < NMIN)
+ return (a[n/2])
+
+ # Compute the mode. The array must be sorted. Consider a
+ # range of values about the median point. Use a bin size which
+ # is ZBIN of the range. Step the bin limits in ZSTEP fraction of
+ # the bin size.
+
+ i = 1 + n * (1. - ZRANGE) / 2.
+ j = 1 + n * (1. + ZRANGE) / 2.
+ z1 = a[i]
+ z2 = a[j]
+ if (fp_equalr (z1, z2)) {
+ mode = z1
+ return (mode)
+ }
+
+ zstep = ZSTEP * (z2 - z1)
+ zbin = ZBIN * (z2 - z1)
+ $if (datatype == sil)
+ zstep = max (1., zstep)
+ zbin = max (1., zbin)
+ $endif
+
+ z1 = z1 - zstep
+ k = i
+ nmax = 0
+ repeat {
+ z1 = z1 + zstep
+ z2 = z1 + zbin
+ for (; i < j && a[i] < z1; i=i+1)
+ ;
+ for (; k < j && a[k] < z2; k=k+1)
+ ;
+ if (k - i > nmax) {
+ nmax = k - i
+ mode = a[(i+k)/2]
+ }
+ } until (k >= j)
+
+ return (mode)
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/mkpkg b/pkg/images/immatch/src/imcombine/src/mkpkg
new file mode 100644
index 00000000..5f53d4b8
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/mkpkg
@@ -0,0 +1,67 @@
+# Make the IMCOMBINE library.
+
+update:
+ $checkout libimc.a lib$
+ $update libimc.a
+ $checkin libimc.a lib$
+ ;
+
+generic:
+ $set GEN = "$$generic -k"
+
+ $ifolder (generic/icaclip.x, icaclip.gx)
+ $(GEN) icaclip.gx -o generic/icaclip.x $endif
+ $ifolder (generic/icaverage.x, icaverage.gx)
+ $(GEN) icaverage.gx -o generic/icaverage.x $endif
+ $ifolder (generic/icquad.x, icquad.gx)
+ $(GEN) icquad.gx -o generic/icquad.x $endif
+ $ifolder (generic/icnmodel.x, icnmodel.gx)
+ $(GEN) icnmodel.gx -o generic/icnmodel.x $endif
+ $ifolder (generic/iccclip.x, iccclip.gx)
+ $(GEN) iccclip.gx -o generic/iccclip.x $endif
+ $ifolder (generic/icgdata.x, icgdata.gx)
+ $(GEN) icgdata.gx -o generic/icgdata.x $endif
+ $ifolder (generic/icgrow.x, icgrow.gx)
+ $(GEN) icgrow.gx -o generic/icgrow.x $endif
+ $ifolder (generic/icmedian.x, icmedian.gx)
+ $(GEN) icmedian.gx -o generic/icmedian.x $endif
+ $ifolder (generic/icmm.x, icmm.gx)
+ $(GEN) icmm.gx -o generic/icmm.x $endif
+ $ifolder (generic/icomb.x, icomb.gx)
+ $(GEN) icomb.gx -o generic/icomb.x $endif
+ $ifolder (generic/icpclip.x, icpclip.gx)
+ $(GEN) icpclip.gx -o generic/icpclip.x $endif
+ $ifolder (generic/icsclip.x, icsclip.gx)
+ $(GEN) icsclip.gx -o generic/icsclip.x $endif
+ $ifolder (generic/icsigma.x, icsigma.gx)
+ $(GEN) icsigma.gx -o generic/icsigma.x $endif
+ $ifolder (generic/icsort.x, icsort.gx)
+ $(GEN) icsort.gx -o generic/icsort.x $endif
+ $ifolder (generic/icstat.x, icstat.gx)
+ $(GEN) icstat.gx -o generic/icstat.x $endif
+
+ $ifolder (generic/xtimmap.x, xtimmap.gx)
+ $(GEN) xtimmap.gx -o generic/xtimmap.x $endif
+ ;
+
+libimc.a:
+ $ifeq (USE_GENERIC, yes) $call generic $endif
+
+ @generic
+
+ icemask.x <imhdr.h> <mach.h>
+ icgscale.x icombine.com icombine.h
+ ichdr.x <imset.h>
+ icimstack.x <error.h> <imhdr.h>
+ iclog.x icmask.h icombine.com icombine.h <imhdr.h> <imset.h>\
+ <mach.h>
+ icmask.x icmask.h icombine.com icombine.h <imhdr.h> <pmset.h>
+ icombine.x icombine.com icombine.h <error.h> <imhdr.h> <imset.h>
+ icpmmap.x <pmset.h>
+ icrmasks.x <imhdr.h>
+ icscale.x icombine.com icombine.h <imhdr.h> <imset.h>
+ icsection.x <ctype.h>
+ icsetout.x icombine.com <imhdr.h> <imset.h> <mwset.h>
+ tymax.x <mach.h>
+ xtprocid.x
+ ;
diff --git a/pkg/images/immatch/src/imcombine/src/tymax.x b/pkg/images/immatch/src/imcombine/src/tymax.x
new file mode 100644
index 00000000..a7f4f469
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/tymax.x
@@ -0,0 +1,27 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <mach.h>
+
+
+# TY_MAX -- Return the datatype of highest precedence.
+
+int procedure ty_max (type1, type2)
+
+int type1, type2 # Datatypes
+
+int i, j, type, order[8]
+data order/TY_SHORT,TY_USHORT,TY_INT,TY_LONG,TY_REAL,TY_DOUBLE,TY_COMPLEX,TY_REAL/
+
+begin
+ for (i=1; (i<=7) && (type1!=order[i]); i=i+1)
+ ;
+ for (j=1; (j<=7) && (type2!=order[j]); j=j+1)
+ ;
+ type = order[max(i,j)]
+
+ # Special case of mixing short and unsigned short.
+ if (type == TY_USHORT && type1 != type2)
+ type = TY_INT
+
+ return (type)
+end
diff --git a/pkg/images/immatch/src/imcombine/src/xtimmap.gx b/pkg/images/immatch/src/imcombine/src/xtimmap.gx
new file mode 100644
index 00000000..2e6cfb1e
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/xtimmap.gx
@@ -0,0 +1,634 @@
+include <syserr.h>
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+include <config.h>
+
+# The following is for compiling under V2.11.
+define IM_BUFFRAC IM_BUFSIZE
+include <imset.h>
+
+define VERBOSE false
+
+# These routines maintain an arbitrary number of indexed "open" images which
+# must be READ_ONLY. The calling program may use the returned pointer for
+# header accesses but must call xt_opix before I/O. Subsequent calls to
+# xt_opix may invalidate the pointer. The xt_imunmap call will free memory.
+
+define MAX_OPENIM (LAST_FD-16) # Maximum images kept open
+define MAX_OPENPIX 45 # Maximum pixel files kept open
+
+define XT_SZIMNAME 299 # Size of IMNAME string
+define XT_LEN 179 # Structure length
+define XT_IMNAME Memc[P2C($1)] # Image name
+define XT_ARG Memi[$1+150] # IMMAP header argument
+define XT_IM Memi[$1+151] # IMIO pointer
+define XT_HDR Memi[$1+152] # Copy of IMIO pointer
+define XT_CLOSEFD Memi[$1+153] # Close FD?
+define XT_FLAG Memi[$1+154] # Flag
+define XT_BUFSIZE Memi[$1+155] # Buffer size
+define XT_BUF Memi[$1+156] # Data buffer
+define XT_BTYPE Memi[$1+157] # Data buffer type
+define XT_VS Memi[$1+157+$2] # Start vector (10)
+define XT_VE Memi[$1+167+$2] # End vector (10)
+
+# Options
+define XT_MAPUNMAP 1 # Map and unmap images.
+
+# XT_IMMAP -- Map an image and save it as an indexed open image.
+# The returned pointer may be used for header access but not I/O.
+# The indexed image is closed by xt_imunmap.
+
+pointer procedure xt_immap (imname, acmode, hdr_arg, index, retry)
+
+char imname[ARB] #I Image name
+int acmode #I Access mode
+int hdr_arg #I Header argument
+int index #I Save index
+int retry #I Retry counter
+pointer im #O Image pointer (returned)
+
+int i, envgeti()
+pointer xt, xt_opix()
+errchk xt_opix
+
+int first_time
+data first_time /YES/
+
+include "xtimmap.com"
+
+begin
+ if (acmode != READ_ONLY)
+ call error (1, "XT_IMMAP: Only READ_ONLY allowed")
+
+ # Set maximum number of open images based on retry.
+ if (retry > 0)
+ max_openim = min (1024, MAX_OPENIM) / retry
+ else
+ max_openim = MAX_OPENIM
+
+ # Initialize once per process.
+ if (first_time == YES) {
+ iferr (option = envgeti ("imcombine_option"))
+ option = 1
+ min_open = 1
+ nopen = 0
+ nopenpix = 0
+ nalloc = max_openim
+ call calloc (ims, nalloc, TY_POINTER)
+ first_time = NO
+ }
+
+ # Free image if needed.
+ call xt_imunmap (NULL, index)
+
+ # Allocate structure.
+ if (index > nalloc) {
+ i = nalloc
+ nalloc = index + max_openim
+ call realloc (ims, nalloc, TY_STRUCT)
+ call amovki (NULL, Memi[ims+i], nalloc-i)
+ }
+ call calloc (xt, XT_LEN, TY_STRUCT)
+ Memi[ims+index-1] = xt
+
+ # Initialize.
+ call strcpy (imname, XT_IMNAME(xt), XT_SZIMNAME)
+ XT_ARG(xt) = hdr_arg
+ XT_IM(xt) = NULL
+ XT_HDR(xt) = NULL
+
+ # Open image.
+ last_flag = 0
+ im = xt_opix (NULL, index, 0)
+
+ # Make copy of IMIO pointer for header keyword access.
+ call malloc (XT_HDR(xt), LEN_IMDES+IM_HDRLEN(im)+1, TY_STRUCT)
+ call amovi (Memi[im], Memi[XT_HDR(xt)], LEN_IMDES)
+ call amovi (IM_MAGIC(im), IM_MAGIC(XT_HDR(xt)), IM_HDRLEN(im)+1)
+
+ return (XT_HDR(xt))
+end
+
+
+# XT_OPIX -- Open the image for I/O.
+# If the image has not been mapped return the default pointer.
+
+pointer procedure xt_opix (imdef, index, flag)
+
+int index #I index
+pointer imdef #I Default pointer
+int flag #I Flag
+
+int i, open(), imstati()
+pointer im, xt, xt1, immap()
+errchk open, immap, imunmap
+
+include "xtimmap.com"
+
+begin
+ # Get index pointer.
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ # Use default pointer if index has not been mapped.
+ if (xt == NULL)
+ return (imdef)
+
+ # Close images not accessed during previous line.
+ # In normal usage this should only occur once per line over all
+ # indexed images.
+ if (flag != last_flag) {
+ do i = 1, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL || XT_FLAG(xt1) == last_flag)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: xt_opix imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ call mfree (XT_BUF(xt1), XT_BTYPE(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ }
+
+ # Optimize the file I/O.
+ do i = nalloc, 1, -1 {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ if (nopenpix < MAX_OPENPIX) {
+ if (XT_CLOSEFD(xt1) == NO)
+ next
+ XT_CLOSEFD(xt1) = NO
+ call imseti (im, IM_CLOSEFD, NO)
+ nopenpix = nopenpix + 1
+ }
+ }
+ last_flag = flag
+ }
+
+ # Return pointer for already opened images.
+ im = XT_IM(xt)
+ if (im != NULL) {
+ XT_FLAG(xt) = flag
+ return (im)
+ }
+
+ # Handle more images than the maximum that can be open at one time.
+ if (nopen >= max_openim) {
+ if (option == XT_MAPUNMAP || flag == 0) {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ min_open = i + 1
+ break
+ }
+ if (index <= min_open)
+ min_open = index
+ else {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ break
+ }
+ }
+ } else {
+ # Check here because we can't catch error in immap.
+ i = open ("dev$null", READ_ONLY, BINARY_FILE)
+ call close (i)
+ if (i == LAST_FD - 1)
+ call error (SYS_FTOOMANYFILES, "Too many open files")
+ }
+ }
+
+ # Open image.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_opix immap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ im = immap (XT_IMNAME(xt), READ_ONLY, XT_ARG(xt))
+ XT_IM(xt) = im
+ if (!IS_INDEFI(XT_BUFSIZE(xt)))
+ call imseti (im, IM_BUFSIZE, XT_BUFSIZE(xt))
+ else
+ XT_BUFSIZE(xt) = imstati (im, IM_BUFSIZE)
+ nopen = nopen + 1
+ XT_CLOSEFD(xt) = YES
+ if (nopenpix < MAX_OPENPIX) {
+ XT_CLOSEFD(xt) = NO
+ nopenpix = nopenpix + 1
+ }
+ if (XT_CLOSEFD(xt) == YES)
+ call imseti (im, IM_CLOSEFD, YES)
+ XT_FLAG(xt) = flag
+
+ return (im)
+end
+
+
+# XT_CPIX -- Close image.
+
+procedure xt_cpix (index)
+
+int index #I index
+
+pointer xt
+errchk imunmap
+
+include "xtimmap.com"
+
+begin
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ if (xt == NULL)
+ return
+
+ if (XT_IM(xt) != NULL) {
+ if (VERBOSE) {
+ call eprintf ("%d: xt_cpix imunmap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ call imunmap (XT_IM(xt))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt) == NO)
+ nopenpix = nopenpix - 1
+ }
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+end
+
+
+# XT_IMSETI -- Set IMIO value.
+
+procedure xt_imseti (index, param, value)
+
+int index #I index
+int param #I IMSET parameter
+int value #I Value
+
+pointer xt
+bool streq()
+
+include "xtimmap.com"
+
+begin
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ if (xt == NULL) {
+ if (streq (param, "option"))
+ option = value
+ } else {
+ if (streq (param, "bufsize")) {
+ XT_BUFSIZE(xt) = value
+ if (XT_IM(xt) != NULL) {
+ call imseti (XT_IM(xt), IM_BUFFRAC, 0)
+ call imseti (XT_IM(xt), IM_BUFSIZE, value)
+ }
+ }
+ }
+end
+
+
+# XT_IMUNMAP -- Unmap indexed open image.
+# The header pointer is set to NULL to indicate the image has been closed.
+
+procedure xt_imunmap (im, index)
+
+int im #U IMIO header pointer
+int index #I index
+
+pointer xt
+errchk imunmap
+
+include "xtimmap.com"
+
+begin
+ # Check for an indexed image. If it is not unmap the pointer
+ # as a regular IMIO pointer.
+
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+ if (xt == NULL) {
+ if (im != NULL)
+ call imunmap (im)
+ return
+ }
+
+ # Close indexed image.
+ if (XT_IM(xt) != NULL) {
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imunmap imunmap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ iferr (call imunmap (XT_IM(xt))) {
+ XT_IM(xt) = NULL
+ call erract (EA_WARN)
+ }
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt) == NO)
+ nopenpix = nopenpix - 1
+ if (index == min_open)
+ min_open = 1
+ }
+
+ # Free any buffered memory.
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+
+ # Free header pointer. Note that if the supplied pointer is not
+ # header pointer then it is not set to NULL.
+ if (XT_HDR(xt) == im)
+ im = NULL
+ call mfree (XT_HDR(xt), TY_STRUCT)
+
+ # Free save structure.
+ call mfree (Memi[ims+index-1], TY_STRUCT)
+ Memi[ims+index-1] = NULL
+end
+
+
+# XT_MINHDR -- Minimize header assuming keywords will not be accessed.
+
+procedure xt_minhdr (index)
+
+int index #I index
+
+pointer xt
+errchk realloc
+
+include "xtimmap.com"
+
+begin
+ # Check for an indexed image. If it is not unmap the pointer
+ # as a regular IMIO pointer.
+
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+ if (xt == NULL)
+ return
+
+ # Minimize header pointer.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_minhdr %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ call realloc (XT_HDR(xt), IMU+1, TY_STRUCT)
+ if (XT_IM(xt) != NULL)
+ call realloc (XT_IM(xt), IMU+1, TY_STRUCT)
+end
+
+
+# XT_REINDEX -- Reindex open images.
+# This is used when some images are closed by xt_imunmap. It is up to
+# the calling program to reindex the header pointers and to subsequently
+# use the new index values.
+
+procedure xt_reindex ()
+
+int old, new
+
+include "xtimmap.com"
+
+begin
+ new = 0
+ do old = 0, nalloc-1 {
+ if (Memi[ims+old] == NULL)
+ next
+ Memi[ims+new] = Memi[ims+old]
+ new = new + 1
+ }
+ do old = new, nalloc-1
+ Memi[ims+old] = NULL
+end
+
+
+$for(sird)
+# XT_IMGNL -- Return the next line for the indexed image.
+# Possibly unmap another image if too many files are open.
+# Buffer data when an image is unmmaped to minimize the mapping of images.
+# If the requested index has not been mapped use the default pointer.
+
+int procedure xt_imgnl$t (imdef, index, buf, v, flag)
+
+pointer imdef #I Default pointer
+int index #I index
+pointer buf #O Data buffer
+long v[ARB] #I Line vector
+int flag #I Flag (=output line)
+
+int i, j, nc, nl, open(), imgnl$t(), sizeof(), imloop()
+pointer im, xt, xt1, ptr, immap(), imggs$t()
+errchk open, immap, imgnl$t, imggs$t, imunmap
+
+long unit_v[IM_MAXDIM]
+data unit_v /IM_MAXDIM * 1/
+
+include "xtimmap.com"
+
+begin
+ # Get index pointer.
+ xt = NULL
+ if (index <= nalloc && index > 0)
+ xt = Memi[ims+index-1]
+
+ # Use default pointer if index has not been mapped.
+ if (xt == NULL)
+ return (imgnl$t (imdef, buf, v))
+
+ # Close images not accessed during previous line.
+ # In normal usage this should only occur once per line over all
+ # indexed images.
+ if (flag != last_flag) {
+ do i = 1, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL || XT_FLAG(xt1) == last_flag)
+ next
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ call mfree (XT_BUF(xt1), XT_BTYPE(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ }
+
+ # Optimize the file I/O.
+ do i = nalloc, 1, -1 {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+ min_open = i
+ if (nopenpix < MAX_OPENPIX) {
+ if (XT_CLOSEFD(xt1) == NO)
+ next
+ XT_CLOSEFD(xt1) = NO
+ call imseti (im, IM_CLOSEFD, NO)
+ nopenpix = nopenpix + 1
+ }
+ }
+ last_flag = flag
+ }
+
+ # Use IMIO for already opened images.
+ im = XT_IM(xt)
+ if (im != NULL) {
+ XT_FLAG(xt) = flag
+ return (imgnl$t (im, buf, v))
+ }
+
+ # If the image is not currently mapped use the stored header.
+ im = XT_HDR(xt)
+
+ # Check for EOF.
+ i = IM_NDIM(im)
+ if (v[i] > IM_LEN(im,i))
+ return (EOF)
+
+ # Check for buffered data.
+ if (XT_BUF(xt) != NULL) {
+ if (v[2] >= XT_VS(xt,2) && v[2] <= XT_VE(xt,2)) {
+ if (XT_BTYPE(xt) != TY_PIXEL)
+ call error (1, "Cannot mix data types")
+ nc = IM_LEN(im,1)
+ buf = XT_BUF(xt) + (v[2]-XT_VS(xt,2)) * IM_LEN(im,1)
+ XT_FLAG(xt) = flag
+ if (i == 1)
+ v[1] = nc + 1
+ else
+ j = imloop (v, unit_v, IM_LEN(im,1), unit_v, i)
+ return (nc)
+ }
+ }
+
+ # Handle more images than the maximum that can be open at one time.
+ if (nopen >= max_openim) {
+ if (option == XT_MAPUNMAP || v[2] == 0) {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ im = XT_IM(xt1)
+ if (im == NULL)
+ next
+
+ # Buffer some number of lines.
+ nl = XT_BUFSIZE(xt1) / sizeof (TY_PIXEL) / IM_LEN(im,1)
+ if (nl > 1) {
+ nc = IM_LEN(im,1)
+ call amovl (v, XT_VS(xt1,1), IM_MAXDIM)
+ call amovl (v, XT_VE(xt1,1), IM_MAXDIM)
+ XT_VS(xt1,1) = 1
+ XT_VE(xt1,1) = nc
+ XT_VE(xt1,2) = min (XT_VS(xt1,2)+(nl-1), IM_LEN(im,2))
+ nl = XT_VE(xt1,2) - XT_VS(xt1,2) + 1
+ XT_BTYPE(xt1) = TY_PIXEL
+ call malloc (XT_BUF(xt1), nl*nc, XT_BTYPE(xt1))
+ ptr = imggs$t (im, XT_VS(xt1,1), XT_VE(xt1,1),
+ IM_NDIM(im))
+ call amov$t (Mem$t[ptr], Mem$t[XT_BUF(xt1)], nl*nc)
+ }
+
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl imunmap %s\n")
+ call pargi (i)
+ call pargstr (XT_IMNAME(xt1))
+ }
+ call imunmap (XT_IM(xt1))
+ nopen = nopen - 1
+ if (XT_CLOSEFD(xt1) == NO)
+ nopenpix = nopenpix - 1
+ min_open = i + 1
+ break
+ }
+ if (index <= min_open)
+ min_open = index
+ else {
+ do i = min_open, nalloc {
+ xt1 = Memi[ims+i-1]
+ if (xt1 == NULL)
+ next
+ if (XT_IM(xt1) == NULL)
+ next
+ min_open = i
+ break
+ }
+ }
+ } else {
+ # Check here because we can't catch error in immap.
+ i = open ("dev$null", READ_ONLY, BINARY_FILE)
+ call close (i)
+ if (i == LAST_FD - 1)
+ call error (SYS_FTOOMANYFILES, "Too many open files")
+ }
+ }
+
+ # Open image.
+ if (VERBOSE) {
+ call eprintf ("%d: xt_imgnl immap %s\n")
+ call pargi (index)
+ call pargstr (XT_IMNAME(xt))
+ }
+ im = immap (XT_IMNAME(xt), READ_ONLY, XT_ARG(xt))
+ XT_IM(xt) = im
+ call imseti (im, IM_BUFSIZE, XT_BUFSIZE(xt))
+ call mfree (XT_BUF(xt), XT_BTYPE(xt))
+ nopen = nopen + 1
+ XT_CLOSEFD(xt) = YES
+ if (nopenpix < MAX_OPENPIX) {
+ XT_CLOSEFD(xt) = NO
+ nopenpix = nopenpix + 1
+ }
+ if (XT_CLOSEFD(xt) == YES)
+ call imseti (im, IM_CLOSEFD, YES)
+ XT_FLAG(xt) = flag
+
+ return (imgnl$t (im, buf, v))
+end
+$endfor
diff --git a/pkg/images/immatch/src/imcombine/src/xtprocid.x b/pkg/images/immatch/src/imcombine/src/xtprocid.x
new file mode 100644
index 00000000..0a82d81b
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/src/xtprocid.x
@@ -0,0 +1,38 @@
+# XT_PROCID -- Set or ppdate PROCID keyword.
+
+procedure xt_procid (im)
+
+pointer im #I Image header
+
+int i, j, ver, patmake(), gpatmatch(), strlen(), ctoi()
+pointer sp, pat, str
+
+begin
+ call smark (sp)
+ call salloc (pat, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Get current ID.
+ iferr (call imgstr (im, "PROCID", Memc[str], SZ_LINE)) {
+ iferr (call imgstr (im, "OBSID", Memc[str], SZ_LINE)) {
+ call sfree (sp)
+ return
+ }
+ }
+
+ # Set new PROCID.
+ ver = 0
+ i = patmake ("V[0-9]*$", Memc[pat], SZ_LINE)
+ if (gpatmatch (Memc[str], Memc[pat], i, j) == 0)
+ ;
+ if (j > 0) {
+ j = i+1
+ if (ctoi (Memc[str], j, ver) == 0)
+ ver = 0
+ i = i - 1
+ } else
+ i = strlen (Memc[str])
+ call sprintf (Memc[str+i], SZ_LINE, "V%d")
+ call pargi (ver+1)
+ call imastr (im, "PROCID", Memc[str])
+end
diff --git a/pkg/images/immatch/src/imcombine/t_imcombine.x b/pkg/images/immatch/src/imcombine/t_imcombine.x
new file mode 100644
index 00000000..d3774958
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/t_imcombine.x
@@ -0,0 +1,230 @@
+# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+
+include <error.h>
+include <mach.h>
+include <imhdr.h>
+include "src/icombine.h"
+
+
+# T_IMCOMBINE - This task combines a list of images into an output image
+# and an optional sigma image. There are many combining options from
+# which to choose.
+
+procedure t_imcombine ()
+
+pointer sp, fname, output, headers, bmask, rmask, sigma, nrmask, emask, logfile
+pointer scales, zeros, wts, im
+int n, input, ilist, olist, hlist, blist, rlist, slist, nrlist, elist
+
+bool clgetb()
+real clgetr()
+int clgwrd(), clgeti(), imtopenp(), imtopen(), imtgetim(), imtlen()
+pointer immap()
+errchk immap, icombine
+
+include "src/icombine.com"
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (output, SZ_FNAME, TY_CHAR)
+ call salloc (headers, SZ_FNAME, TY_CHAR)
+ call salloc (bmask, SZ_FNAME, TY_CHAR)
+ call salloc (rmask, SZ_FNAME, TY_CHAR)
+ call salloc (nrmask, SZ_FNAME, TY_CHAR)
+ call salloc (emask, SZ_FNAME, TY_CHAR)
+ call salloc (sigma, SZ_FNAME, TY_CHAR)
+ call salloc (expkeyword, SZ_FNAME, TY_CHAR)
+ call salloc (statsec, SZ_FNAME, TY_CHAR)
+ call salloc (gain, SZ_FNAME, TY_CHAR)
+ call salloc (rdnoise, SZ_FNAME, TY_CHAR)
+ call salloc (snoise, SZ_FNAME, TY_CHAR)
+ call salloc (logfile, SZ_FNAME, TY_CHAR)
+
+ # Get task parameters. Some additional parameters are obtained later.
+ ilist = imtopenp ("input")
+ olist = imtopenp ("output")
+ hlist = imtopenp ("headers")
+ blist = imtopenp ("bpmasks")
+ rlist = imtopenp ("rejmasks")
+ nrlist = imtopenp ("nrejmasks")
+ elist = imtopenp ("expmasks")
+ slist = imtopenp ("sigmas")
+ call clgstr ("logfile", Memc[logfile], SZ_FNAME)
+
+ project = clgetb ("project")
+ combine = clgwrd ("combine", Memc[fname], SZ_FNAME, COMBINE)
+ if (combine == MEDIAN || combine == LMEDIAN) {
+ if (combine == MEDIAN)
+ medtype = MEDAVG
+ else {
+ medtype = MEDLOW
+ combine = MEDIAN
+ }
+ }
+ reject = clgwrd ("reject", Memc[fname], SZ_FNAME, REJECT)
+ blank = clgetr ("blank")
+ call clgstr ("expname", Memc[expkeyword], SZ_FNAME)
+ call clgstr ("statsec", Memc[statsec], SZ_FNAME)
+ call clgstr ("gain", Memc[gain], SZ_FNAME)
+ call clgstr ("rdnoise", Memc[rdnoise], SZ_FNAME)
+ call clgstr ("snoise", Memc[snoise], SZ_FNAME)
+ lthresh = clgetr ("lthreshold")
+ hthresh = clgetr ("hthreshold")
+ lsigma = clgetr ("lsigma")
+ hsigma = clgetr ("hsigma")
+ pclip = clgetr ("pclip")
+ flow = clgetr ("nlow")
+ fhigh = clgetr ("nhigh")
+ nkeep = clgeti ("nkeep")
+ grow = clgetr ("grow")
+ mclip = clgetb ("mclip")
+ sigscale = clgetr ("sigscale")
+ verbose = false
+
+ # Check lists.
+ n = imtlen (ilist)
+ if (n == 0)
+ call error (1, "No input images to combine")
+
+ if (project) {
+ if (imtlen (olist) != n)
+ call error (1, "Wrong number of output images")
+ if (imtlen (hlist) != 0 && imtlen (hlist) != n)
+ call error (1, "Wrong number of header files")
+ if (imtlen (blist) != 0 && imtlen (blist) != n)
+ call error (1, "Wrong number of bad pixel masks")
+ if (imtlen (rlist) != 0 && imtlen (rlist) != n)
+ call error (1, "Wrong number of rejection masks")
+ if (imtlen (nrlist) > 0 && imtlen (nrlist) != n)
+ call error (1, "Wrong number of number rejected masks")
+ if (imtlen (elist) > 0 && imtlen (elist) != n)
+ call error (1, "Wrong number of exposure masks")
+ if (imtlen (slist) > 0 && imtlen (slist) != n)
+ call error (1, "Wrong number of sigma images")
+ } else {
+ if (imtlen (olist) != 1)
+ call error (1, "Wrong number of output images")
+ if (imtlen (hlist) > 1)
+ call error (1, "Wrong number of header files")
+ if (imtlen (blist) > 1)
+ call error (1, "Wrong number of bad pixel masks")
+ if (imtlen (rlist) > 1)
+ call error (1, "Wrong number of rejection masks")
+ if (imtlen (nrlist) > 1)
+ call error (1, "Wrong number of number rejected masks")
+ if (imtlen (elist) > 1)
+ call error (1, "Wrong number of exposure masks")
+ if (imtlen (slist) > 1)
+ call error (1, "Wrong number of sigma images")
+ }
+
+ # Check parameters, map INDEFs, and set threshold flag
+ if (pclip == 0. && reject == PCLIP)
+ call error (1, "Pclip parameter may not be zero")
+ if (IS_INDEFR (blank))
+ blank = 0.
+ if (IS_INDEFR (lsigma))
+ lsigma = MAX_REAL
+ if (IS_INDEFR (hsigma))
+ hsigma = MAX_REAL
+ if (IS_INDEFR (pclip))
+ pclip = -0.5
+ if (IS_INDEFR (flow))
+ flow = 0
+ if (IS_INDEFR (fhigh))
+ fhigh = 0
+ if (IS_INDEFR (grow))
+ grow = 0.
+ if (IS_INDEF (sigscale))
+ sigscale = 0.
+
+ if (IS_INDEF(lthresh) && IS_INDEF(hthresh))
+ dothresh = false
+ else {
+ dothresh = true
+ if (IS_INDEF(lthresh))
+ lthresh = -MAX_REAL
+ if (IS_INDEF(hthresh))
+ hthresh = MAX_REAL
+ }
+
+ # Loop through image lists.
+ while (imtgetim (ilist, Memc[fname], SZ_FNAME) != EOF) {
+ iferr {
+ scales = NULL; input = ilist
+
+ if (imtgetim (olist, Memc[output], SZ_FNAME) == EOF) {
+ if (project) {
+ call sprintf (Memc[output], SZ_FNAME,
+ "IMCOMBINE: No output image for %s")
+ call pargstr (Memc[fname])
+ call error (1, Memc[output])
+ } else
+ call error (1, "IMCOMBINE: No output image")
+ }
+ if (imtgetim (hlist, Memc[headers], SZ_FNAME) == EOF)
+ Memc[headers] = EOS
+ if (imtgetim (blist, Memc[bmask], SZ_FNAME) == EOF)
+ Memc[bmask] = EOS
+ if (imtgetim (rlist, Memc[rmask], SZ_FNAME) == EOF)
+ Memc[rmask] = EOS
+ if (imtgetim (nrlist, Memc[nrmask], SZ_FNAME) == EOF)
+ Memc[nrmask] = EOS
+ if (imtgetim (elist, Memc[emask], SZ_FNAME) == EOF)
+ Memc[emask] = EOS
+ if (imtgetim (slist, Memc[sigma], SZ_FNAME) == EOF)
+ Memc[sigma] = EOS
+
+ # Set the input list and initialize the scaling factors.
+ if (project) {
+ im = immap (Memc[fname], READ_ONLY, 0)
+ if (IM_NDIM(im) == 1)
+ n = 0
+ else
+ n = IM_LEN(im,IM_NDIM(im))
+ call imunmap (im)
+ if (n == 0) {
+ call sprintf (Memc[output], SZ_FNAME,
+ "IMCOMBINE: Can't project one dimensional image %s")
+ call pargstr (Memc[fname])
+ call error (1, Memc[output])
+ }
+ input = imtopen (Memc[fname])
+ } else {
+ call imtrew (ilist)
+ n = imtlen (ilist)
+ input = ilist
+ }
+
+ # Allocate and initialize scaling factors.
+ call malloc (scales, 3*n, TY_REAL)
+ zeros = scales + n
+ wts = scales + 2 * n
+ call amovkr (INDEFR, Memr[scales], 3*n)
+
+ call icombine (input, Memc[output], Memc[headers], Memc[bmask],
+ Memc[rmask], Memc[nrmask], Memc[emask], Memc[sigma],
+ Memc[logfile], Memr[scales], Memr[zeros], Memr[wts],
+ NO, NO, NO)
+
+ } then
+ call erract (EA_WARN)
+
+ if (input != ilist)
+ call imtclose (input)
+ call mfree (scales, TY_REAL)
+ if (!project)
+ break
+ }
+
+ call imtclose (ilist)
+ call imtclose (olist)
+ call imtclose (hlist)
+ call imtclose (blist)
+ call imtclose (rlist)
+ call imtclose (nrlist)
+ call imtclose (elist)
+ call imtclose (slist)
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/imcombine/x_imcombine.x b/pkg/images/immatch/src/imcombine/x_imcombine.x
new file mode 100644
index 00000000..a85e34f6
--- /dev/null
+++ b/pkg/images/immatch/src/imcombine/x_imcombine.x
@@ -0,0 +1 @@
+task imcombine = t_imcombine
diff --git a/pkg/images/immatch/src/linmatch/linmatch.h b/pkg/images/immatch/src/linmatch/linmatch.h
new file mode 100644
index 00000000..0f776901
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/linmatch.h
@@ -0,0 +1,298 @@
+# Header file for LINSCALE
+
+define LEN_LSSTRUCT (70 + 12 * SZ_FNAME + 12)
+
+# Quantities that define the current region and the number of regions
+
+define LS_CNREGION Memi[$1] # the current region
+define LS_NREGIONS Memi[$1+1] # the number of regions
+define LS_MAXNREGIONS Memi[$1+2] # the maximum number of regions
+
+# Quantities that are dependent on the number of regions
+
+define LS_RC1 Memi[$1+3] # pointers to first columns of regions
+define LS_RC2 Memi[$1+4] # pointers to last columns of regions
+define LS_RL1 Memi[$1+5] # pointer to first lines of regions
+define LS_RL2 Memi[$1+6] # pointers to last lines of regions
+define LS_RXSTEP Memi[$1+7] # pointers to the x step sizes
+define LS_RYSTEP Memi[$1+8] # pointers to the y step sizes
+define LS_XSHIFT Memr[P2R($1+9)] # the x shift from image to reference
+define LS_YSHIFT Memr[P2R($1+10)] # the y shift from image to reference
+define LS_SXSHIFT Memr[P2R($1+11)] # the x shift from image to reference
+define LS_SYSHIFT Memr[P2R($1+12)] # the y shift from image to reference
+
+define LS_RBUF Memi[$1+14] # pointer to the reference image data
+define LS_RGAIN Memr[P2R($1+15)] # the reference image gain
+define LS_RREADNOISE Memr[P2R($1+16)] # the reference image readout noise
+define LS_RMEAN Memi[$1+17] # pointers to means of ref regions
+define LS_RMEDIAN Memi[$1+18] # pointers to medians of ref regions
+define LS_RMODE Memi[$1+19] # pointers to modes of ref regions
+define LS_RSIGMA Memi[$1+20] # pointers to stdevs of ref regions
+define LS_RSKY Memi[$1+21] # pointers to sky values of ref regions
+define LS_RSKYERR Memi[$1+22] # pointers to sky errors of ref regions
+define LS_RMAG Memi[$1+23] # pointers to magnitudes of ref regions
+define LS_RMAGERR Memi[$1+24] # pointers to mag errors of ref regions
+define LS_RNPTS Memi[$1+25] # pointers to npts of ref regions
+
+define LS_IBUF Memi[$1+27] # pointer to the input image data
+define LS_IGAIN Memr[P2R($1+28)] # the input image gain
+define LS_IREADNOISE Memr[P2R($1+29)] # the input image readout noise
+define LS_IMEAN Memi[$1+30] # pointers to means of image regions
+define LS_IMEDIAN Memi[$1+31] # pointers to medians of image regions
+define LS_IMODE Memi[$1+32] # pointers to modes of image regions
+define LS_ISIGMA Memi[$1+33] # pointers to stdevs of image regions
+define LS_ISKY Memi[$1+34] # pointers to sky values of image regions
+define LS_ISKYERR Memi[$1+35] # pointers to sky errors of image regions
+define LS_IMAG Memi[$1+36] # pointers to magnitudes of image regions
+define LS_IMAGERR Memi[$1+37] # pointers to mag errors of image regions
+define LS_INPTS Memi[$1+38] # pointers to npts of image regions
+
+define LS_RBSCALE Memi[$1+39] # pointers to bscales of regions
+define LS_RBSCALEERR Memi[$1+40] # pointers to bscale errors of regions
+define LS_RBZERO Memi[$1+41] # pointers to bzero errors of regions
+define LS_RBZEROERR Memi[$1+42] # pointers to bzero errors of regions
+define LS_RDELETE Memi[$1+43] # pointer to the delete array
+define LS_RCHI Memi[$1+44] # pointer to the resid array
+
+# Quantities that affect the fitting algorithms
+
+define LS_BSALGORITHM Memi[$1+45] # bscale fitting algorithm
+define LS_BZALGORITHM Memi[$1+46] # bzero fitting algorithm
+define LS_CBZERO Memr[P2R($1+47)] # constant bzero
+define LS_CBSCALE Memr[P2R($1+48)] # constant bscale
+define LS_DNX Memi[$1+49] # x width of data region to extract
+define LS_DNY Memi[$1+50] # y width of data region to extract
+#define LS_PNX Memi[$1+51] # x width of photometry region
+#define LS_PNY Memi[$1+52] # y widht of photometry region
+define LS_DATAMIN Memr[P2R($1+51)] # the minimum good data value
+define LS_DATAMAX Memr[P2R($1+52)] # the maximum good data value
+define LS_MAXITER Memi[$1+53] # maximum number of iterations
+define LS_NREJECT Memi[$1+54] # maximum number of rejections cycles
+define LS_LOREJECT Memr[P2R($1+55)] # low-side sigma rejection criterion
+define LS_HIREJECT Memr[P2R($1+56)] # high-side sigma rejection criterion
+define LS_GAIN Memr[P2R($1+57)] # the constant gain value in e-/adu
+define LS_READNOISE Memr[P2R($1+58)] # the constant readout noise value in e-
+
+# Quantities that define the answers
+
+define LS_TBSCALE Memr[P2R($1+59)] # bzero value
+define LS_TBSCALEERR Memr[P2R($1+60)] # bscale error estimate
+define LS_TBZERO Memr[P2R($1+61)] # bzero value
+define LS_TBZEROERR Memr[P2R($1+62)] # bzero error estimate
+
+# String quantities
+
+define LS_BSSTRING Memc[P2C($1+65)] # bscale string
+define LS_BZSTRING Memc[P2C($1+65+SZ_FNAME+1)] # bzero string
+define LS_CCDGAIN Memc[P2C($1+65+2*SZ_FNAME+2)] # gain keyword
+define LS_CCDREAD Memc[P2C($1+65+3*SZ_FNAME+3)] # readout noise keyword
+define LS_IMAGE Memc[P2C($1+65+4*SZ_FNAME+4)] # input image
+define LS_REFIMAGE Memc[P2C($1+65+5*SZ_FNAME+5)] # reference image
+define LS_REGIONS Memc[P2C($1+65+6*SZ_FNAME+6)] # regions list
+define LS_DATABASE Memc[P2C($1+65+7*SZ_FNAME+7)] # database file
+define LS_OUTIMAGE Memc[P2C($1+65+8*SZ_FNAME+8)] # output image
+define LS_SHIFTSFILE Memc[P2C($1+65+9*SZ_FNAME+9)] # shifts file
+define LS_PHOTFILE Memc[P2C($1+65+10*SZ_FNAME+10)] # shifts file
+define LS_RECORD Memc[P2C($1+65+11*SZ_FNAME+11)] # the record name
+
+
+# Define the bzero and bscale fitting algorithms
+
+define LS_MEAN 1
+define LS_MEDIAN 2
+define LS_MODE 3
+define LS_FIT 4
+define LS_PHOTOMETRY 5
+define LS_FILE 6
+define LS_NUMBER 7
+
+define LS_SCALING "|mean|median|mode|fit|photometry|file|"
+
+# Define the parameters
+
+define CNREGION 1
+define NREGIONS 2
+define MAXNREGIONS 3
+
+define RC1 4
+define RC2 5
+define RL1 6
+define RL2 7
+define RXSTEP 8
+define RYSTEP 9
+define XSHIFT 10
+define YSHIFT 11
+define SXSHIFT 12
+define SYSHIFT 13
+
+define RBUF 14
+define RGAIN 15
+define RREADNOISE 16
+define RMEAN 17
+define RMEDIAN 18
+define RMODE 19
+define RSIGMA 20
+define RSKY 21
+define RSKYERR 22
+define RMAG 23
+define RMAGERR 24
+define RNPTS 25
+
+define IBUF 26
+define IGAIN 27
+define IREADNOISE 28
+define IMEAN 29
+define IMEDIAN 30
+define IMODE 31
+define ISIGMA 32
+define ISKY 33
+define ISKYERR 34
+define IMAG 35
+define IMAGERR 36
+define INPTS 37
+
+define RBSCALE 38
+define RBSCALEERR 39
+define RBZERO 40
+define RBZEROERR 41
+define RDELETE 42
+define RCHI 43
+
+define BZALGORITHM 44
+define BSALGORITHM 45
+define CBZERO 46
+define CBSCALE 47
+define DNX 48
+define DNY 49
+#define PNX 50
+#define PNY 51
+define DATAMIN 50
+define DATAMAX 51
+define MAXITER 52
+
+define NREJECT 53
+define LOREJECT 54
+define HIREJECT 55
+define GAIN 56
+define READNOISE 57
+
+define TBZERO 58
+define TBZEROERR 59
+define TBSCALE 60
+define TBSCALEERR 61
+
+define BSSTRING 62
+define BZSTRING 63
+define CCDGAIN 64
+define CCDREAD 65
+
+define IMAGE 66
+define REFIMAGE 67
+define REGIONS 68
+define DATABASE 69
+define OUTIMAGE 70
+define RECORD 71
+define SHIFTSFILE 72
+define PHOTFILE 73
+
+# Set some default values
+
+define DEF_MAXNREGIONS 100
+define DEF_BZALGORITHM LS_FIT
+define DEF_BSALGORITHM LS_FIT
+define DEF_CBZERO 0.0
+define DEF_CBSCALE 1.0
+define DEF_DNX 31
+define DEF_DNY 31
+define DEF_MAXITER 10
+define DEF_DATAMIN INDEFR
+define DEF_DATAMAX INDEFR
+define DEF_NREJECT 0
+define DEF_LOREJECT INDEFR
+define DEF_HIREJECT INDEFR
+define DEF_GAIN INDEFR
+define DEF_READNOISE INDEFR
+
+# The mode computation parameters.
+
+define LMODE_NMIN 10
+define LMODE_ZRANGE 1.0
+define LMODE_ZBIN 0.1
+define LMODE_ZSTEP 0.01
+define LMODE_HWIDTH 3.0
+
+# The default plot types.
+
+define LS_MMHIST 1
+define LS_MMFIT 2
+define LS_MMRESID 3
+define LS_RIFIT 4
+define LS_RIRESID 5
+define LS_BSZFIT 6
+define LS_BSZRESID 7
+define LS_MAGSKYFIT 8
+define LS_MAGSKYRESID 9
+
+# The bad point deletions code.
+
+define LS_NO 0
+define LS_BADREGION 1
+define LS_BADSIGMA 2
+define LS_DELETED 3
+
+# Commands
+
+define LSCMDS "|input|reference|regions|lintransform|output|photfile|\
+shifts|records|xshift|yshift|dnx|dny|maxnregions|datamin|datamax|\
+maxiter|nreject|loreject|hireject|gain|readnoise|show|markcoords|marksections|"
+
+define LSCMD_IMAGE 1
+define LSCMD_REFIMAGE 2
+define LSCMD_REGIONS 3
+define LSCMD_DATABASE 4
+define LSCMD_OUTIMAGE 5
+define LSCMD_PHOTFILE 6
+define LSCMD_SHIFTSFILE 7
+define LSCMD_RECORD 8
+define LSCMD_XSHIFT 9
+define LSCMD_YSHIFT 10
+define LSCMD_DNX 11
+define LSCMD_DNY 12
+define LSCMD_MAXNREGIONS 13
+define LSCMD_DATAMIN 14
+define LSCMD_DATAMAX 15
+define LSCMD_MAXITER 16
+define LSCMD_NREJECT 17
+define LSCMD_LOREJECT 18
+define LSCMD_HIREJECT 19
+define LSCMD_GAIN 20
+define LSCMD_READNOISE 21
+define LSCMD_SHOW 22
+define LSCMD_MARKCOORDS 23
+define LSCMD_MARKSECTIONS 24
+
+# Keywords
+
+define KY_REFIMAGE "reference"
+define KY_IMAGE "input"
+define KY_REGIONS "regions"
+define KY_DATABASE "lintransform"
+define KY_OUTIMAGE "output"
+define KY_PHOTFILE "photfile"
+define KY_SHIFTSFILE "shifts"
+define KY_RECORD "records"
+define KY_XSHIFT "xshift"
+define KY_YSHIFT "yshift"
+define KY_DNX "dnx"
+define KY_DNY "dny"
+define KY_MAXNREGIONS "maxnregions"
+define KY_DATAMIN "datamin"
+define KY_DATAMAX "datamax"
+define KY_MAXITER "maxiter"
+define KY_NREJECT "nreject"
+define KY_LOREJECT "loreject"
+define KY_HIREJECT "hireject"
+define KY_GAIN "gain"
+define KY_READNOISE "readnoise"
+define KY_NREGIONS "nregions"
+
diff --git a/pkg/images/immatch/src/linmatch/linmatch.key b/pkg/images/immatch/src/linmatch/linmatch.key
new file mode 100644
index 00000000..824f6b26
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/linmatch.key
@@ -0,0 +1,51 @@
+ Interactive Keystroke Commands
+
+? Print help
+: Colon commands
+
+g Draw a plot of the current fit
+i Draw the residuals plot for the current fit
+p Draw a plot of current photometry
+s Draw histograms for the image region nearest the cursor
+l Draw the least squares fit for the image region nearest the cursor
+h Draw histogram plot of each image region in turn
+l Draw least squares fits plot of each image region in turn
+r Redraw the current plot
+d Delete the image region nearest the cursor
+u Undelete the image region nearest the cursor
+f Recompute the intensity matching function
+w Update the task parameters
+q Exit
+
+
+ Colon Commands
+
+:markcoords Mark objects on the display
+:marksections Mark image sections on the display
+:show Show current values of all the parameters
+
+ Show/set Parameters
+
+:input [string] Show/set the current input image
+:reference [string] Show/set the current reference image / phot file
+:regions [string] Show/set the current image regions
+:photfile [string] Show/set the current input photometry file
+:lintransform [string] Show/set the linear transform database file name
+:dnx [value] Show/set the default x size of an image region
+:dny [value] Show/set the default y size of an image region
+:shifts [string] Show/set the current shifts file
+:xshift [value] Show/set the input image x shift
+:yshift [value] Show/set the input image y shift
+:output [string] Show/set the current output image name
+:maxnregions Show the maximum number of objects / regions
+:gain [string] Show/set the gain value / image header keyword
+:readnoise [string] Show/set the readout noise value / image header
+ keyword
+
+:scaling Show the current scaling algorithm
+:datamin [value] Show/set the minimum good data value
+:datamax [value] Show/set the maximum good data value
+:nreject [value] Show/set the maximum number of rejection cycles
+:loreject [value] Show/set low side k-sigma rejection parameter
+:hireject [value] Show/set high side k-sigma rejection parameter
+
diff --git a/pkg/images/immatch/src/linmatch/lsqfit.h b/pkg/images/immatch/src/linmatch/lsqfit.h
new file mode 100644
index 00000000..69691935
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/lsqfit.h
@@ -0,0 +1,18 @@
+# The definitions file for the least squares fitting routines.
+
+define MAX_NFITPARS 7 # number of parameters following
+
+define YINCPT $1[1] # y-intercept
+define EYINCPT $1[2] # error in y-intercept
+define SLOPE $1[3] # slope of fit
+define ESLOPE $1[4] # error in slope
+define CHI $1[5] # mean error of unit weight
+define RMS $1[6] # mean error of unit weight
+
+#define ME1 $1[1] # mean error of unit weight
+#define OFFSET $1[2] # intercept
+#define EOFFSET $1[3] # error in intercept
+#define SLOPE1 $1[4] # slope of fit to first variable
+#define ESLOPE1 $1[5] # error in slope1
+#define SLOPE2 $1[6] # slope of fit to second variable
+#define ESLOPE2 $1[7] # error in slope2
diff --git a/pkg/images/immatch/src/linmatch/mkpkg b/pkg/images/immatch/src/linmatch/mkpkg
new file mode 100644
index 00000000..5a8894f2
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/mkpkg
@@ -0,0 +1,21 @@
+# Make the LINMATCH task
+
+$checkout libpkg.a ../../../
+$update libpkg.a
+$checkin libpkg.a ../../../
+$exit
+
+libpkg.a:
+ rglcolon.x <imhdr.h> <error.h> linmatch.h
+ rgldbio.x linmatch.h
+ rgldelete.x <gset.h> <mach.h> linmatch.h
+ rgliscale.x <imhdr.h> <gset.h> <ctype.h> linmatch.h
+ rglpars.x <lexnum.h> linmatch.h
+ rglplot.x <mach.h> <gset.h> linmatch.h
+ rglregions.x <fset.h> <imhdr.h> <ctype.h> linmatch.h
+ rglscale.x <mach.h> <imhdr.h> linmatch.h lsqfit.h
+ rglshow.x linmatch.h
+ rglsqfit.x <mach.h> lsqfit.h
+ rgltools.x linmatch.h
+ t_linmatch.x <fset.h> <imhdr.h> <imset.h> <error.h> linmatch.h
+ ;
diff --git a/pkg/images/immatch/src/linmatch/rglcolon.x b/pkg/images/immatch/src/linmatch/rglcolon.x
new file mode 100644
index 00000000..8c1d48ef
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rglcolon.x
@@ -0,0 +1,564 @@
+include <imhdr.h>
+include <error.h>
+include "linmatch.h"
+
+# RG_LCOLON -- Show/set the linmatch task algorithm parameters.
+
+procedure rg_lcolon (gd, ls, imr, im1, im2, db, dformat, reglist, rpfd, ipfd,
+ sfd, cmdstr, newref, newimage, newfit, newavg)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to linmatch structure
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer im2 #I pointer to the output image
+pointer db #I pointer to the databas file
+int dformat #I the database file format
+int reglist #I the regions / photometry file descriptor
+int rpfd #I the reference photometry file descriptor
+int ipfd #I the input photometry file descriptor
+int sfd #I the shifts file descriptor
+char cmdstr[ARB] #I command string
+int newref #I/O new reference image
+int newimage #I/O new input image
+int newfit #I/O new fit
+int newavg #I/O new averages
+
+int ncmd, nref, nim, ival, fd
+pointer sp, cmd, str
+real rval
+bool streq()
+int strdic(), rg_lstati(), rg_lregions(), open(), fntopnb(), nscan()
+int rg_lrphot(), access(), rg_lmkxy(), rg_lmkregions()
+pointer immap(), dtmap()
+real rg_lstatr()
+errchk immap(), open(), fntopnb()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get the command.
+ call sscan (cmdstr)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call sfree (sp)
+ return
+ }
+
+ # Process the command.
+ ncmd = strdic (Memc[cmd], Memc[cmd], SZ_LINE, LSCMDS)
+
+ switch (ncmd) {
+
+ case LSCMD_REFIMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_lstats (ls, REFIMAGE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str])
+ } else if (rg_lstati(ls, BSALGORITHM) == LS_PHOTOMETRY ||
+ rg_lstati(ls, BZALGORITHM) == LS_PHOTOMETRY) {
+ if (rpfd != NULL) {
+ call close (rpfd)
+ rpfd = NULL
+ }
+ iferr {
+ rpfd = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ call erract (EA_WARN)
+ rpfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ if (rg_lrphot (rpfd, ls, 1, rg_lstati(ls, MAXNREGIONS),
+ YES) <= 0)
+ ;
+ call seek (ipfd, BOF)
+ if (rg_lrphot (ipfd, ls, 1, rg_lstati(ls, NREGIONS),
+ NO) <= 0)
+ ;
+ } else {
+ nref = rg_lrphot (rpfd, ls, 1, rg_lstati(ls, MAXNREGIONS),
+ YES)
+ if (nref > 0) {
+ call seek (ipfd, BOF)
+ nim = rg_lrphot (ipfd, ls, 1, rg_lstati(ls, NREGIONS),
+ NO)
+ if (nim < nref)
+ call printf ("There are too few input points\n")
+ } else {
+ call close (rpfd)
+ rpfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ if (rg_lrphot (rpfd, ls, 1, rg_lstati(ls, MAXNREGIONS),
+ YES) <= 0)
+ ;
+ call seek (ipfd, BOF)
+ if (rg_lrphot (ipfd, ls, 1, rg_lstati(ls, NREGIONS),
+ NO) <= 0)
+ ;
+ call printf (
+ "The new reference photometry file is empty\n")
+ }
+ call rg_lsets (ls, REFIMAGE, Memc[cmd])
+ newref = YES; newimage = YES; newfit = YES; newavg = YES
+ }
+ } else {
+ if (imr != NULL) {
+ call imunmap (imr)
+ imr = NULL
+ }
+ iferr {
+ imr = immap (Memc[cmd], READ_ONLY, 0)
+ } then {
+ call erract (EA_WARN)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ } else if (IM_NDIM(imr) > 2 || IM_NDIM(imr) != IM_NDIM(im1)) {
+ call printf (
+ "Reference image has the wrong number of dimensions\n")
+ call imunmap (imr)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ } else {
+ call rg_lgain (imr, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,GAIN)))
+ call rg_lsetr (ls, RGAIN, rg_lstatr (ls,GAIN))
+ call rg_lrdnoise (imr, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,READNOISE)))
+ call rg_lsetr (ls, RREADNOISE, rg_lstatr (ls,READNOISE))
+ call rg_lsets (ls, REFIMAGE, Memc[cmd])
+ newref = YES; newimage = YES; newfit = YES; newavg = YES
+ }
+ }
+
+ case LSCMD_IMAGE:
+
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_lstats (ls, IMAGE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (im1 != NULL) {
+ call imunmap (im1)
+ im1 = NULL
+ }
+ iferr {
+ im1 = immap (Memc[cmd], READ_ONLY, 0)
+ } then {
+ call erract (EA_WARN)
+ im1 = immap (Memc[str], READ_ONLY, 0)
+ } else if (IM_NDIM(im1) > 2 || IM_NDIM(im1) != IM_NDIM(imr)) {
+ call printf (
+ "Reference image has the wrong number of dimensions\n")
+ call imunmap (im1)
+ im1 = immap (Memc[str], READ_ONLY, 0)
+ } else {
+ call rg_lgain (im1, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,GAIN)))
+ call rg_lsetr (ls, IGAIN, rg_lstatr (ls,GAIN))
+ call rg_lrdnoise (im1, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,READNOISE)))
+ call rg_lsetr (ls, IREADNOISE, rg_lstatr (ls,READNOISE))
+ call rg_lsets (ls, IMAGE, Memc[cmd])
+ newimage = YES; newref = YES; newfit = YES; newavg = YES
+ }
+ }
+
+ case LSCMD_REGIONS:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_lstats (ls, REGIONS, Memc[str], SZ_FNAME)
+ if (reglist == NULL || nscan() == 1 || (streq (Memc[cmd],
+ Memc[str]) && Memc[cmd] != EOS)) {
+ call printf ("%s [string/file]: %s\n")
+ call pargstr (KY_REGIONS)
+ call pargstr (Memc[str])
+ } else if (rg_lstati(ls, BSALGORITHM) != LS_PHOTOMETRY &&
+ rg_lstati(ls, BZALGORITHM) != LS_PHOTOMETRY) {
+ call fntclsb (reglist)
+ iferr {
+ reglist = fntopnb (Memc[cmd], NO)
+ } then {
+ reglist = fntopnb (Memc[str], NO)
+ } else {
+ if (rg_lregions (reglist, imr, ls, 1, NO) > 0)
+ ;
+ call rg_lsets (ls, REGIONS, Memc[cmd])
+ newimage = YES; newref = YES; newfit = YES; newavg = YES
+ }
+ }
+
+ case LSCMD_PHOTFILE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_lstats (ls, PHOTFILE, Memc[str], SZ_FNAME)
+ if (ipfd == NULL || Memc[cmd] == EOS || streq (Memc[cmd],
+ Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_PHOTFILE)
+ call pargstr (Memc[str])
+ } else {
+ if (ipfd != NULL) {
+ call close (ipfd)
+ ipfd = NULL
+ }
+ iferr {
+ ipfd = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ call erract (EA_WARN)
+ ipfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ } else {
+ nim = rg_lrphot (ipfd, ls, 1, rg_lstati(ls, NREGIONS),
+ NO)
+ if (nim > 0) {
+ call rg_lsets (ls, PHOTFILE, Memc[cmd])
+ newref = YES; newimage = YES
+ newfit = YES; newavg = YES
+ } else {
+ call close (ipfd)
+ ipfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ nim = rg_lrphot (ipfd, ls, 1, rg_lstati(ls, NREGIONS),
+ NO)
+ }
+ }
+ }
+
+ case LSCMD_SHIFTSFILE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_lstats (ls, SHIFTSFILE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd],
+ Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_SHIFTSFILE)
+ call pargstr (Memc[str])
+ } else {
+ if (sfd != NULL) {
+ call close (sfd)
+ sfd = NULL
+ }
+ iferr {
+ sfd = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ call erract (EA_WARN)
+ sfd = open (Memc[str], READ_ONLY, sfd)
+ } else {
+ call rg_lgshift (sfd, ls)
+ call rg_lstats (ls, SHIFTSFILE, Memc[cmd], SZ_FNAME)
+ }
+ }
+
+ case LSCMD_OUTIMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_lstats (ls, OUTIMAGE, Memc[str], SZ_FNAME)
+ if (im2 == NULL || Memc[cmd] == EOS || streq (Memc[cmd],
+ Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_OUTIMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (im2 != NULL) {
+ call imunmap (im2)
+ im2 = NULL
+ }
+ iferr {
+ im2 = immap (Memc[cmd], NEW_COPY, im1)
+ } then {
+ call erract (EA_WARN)
+ im2 = immap (Memc[str], NEW_COPY, im1)
+ } else {
+ call rg_lsets (ls, OUTIMAGE, Memc[cmd])
+ }
+ }
+
+ case LSCMD_DATABASE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_lstats (ls, DATABASE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_DATABASE)
+ call pargstr (Memc[str])
+ } else {
+ if (db != NULL) {
+ if (dformat == YES)
+ call dtunmap (db)
+ else
+ call close (db)
+ db = NULL
+ }
+ iferr {
+ if (dformat == YES)
+ db = dtmap (Memc[cmd], APPEND)
+ else
+ db = open (Memc[cmd], NEW_FILE, TEXT_FILE)
+ } then {
+ call erract (EA_WARN)
+ if (dformat == YES)
+ db = dtmap (Memc[str], APPEND)
+ else
+ db = open (Memc[str], APPEND, TEXT_FILE)
+ } else {
+ call rg_lsets (ls, DATABASE, Memc[cmd])
+ }
+ }
+
+ CASE LSCMD_RECORD:
+ call gargstr (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_lstats (ls, RECORD, Memc[str], SZ_FNAME)
+ call printf ("%s: %s\n")
+ call pargstr (KY_RECORD)
+ call pargstr (Memc[str])
+ } else
+ call rg_lsets (ls, RECORD, Memc[cmd])
+
+ case LSCMD_XSHIFT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_XSHIFT)
+ call pargr (rg_lstatr (ls, XSHIFT))
+ } else {
+ call rg_lsetr (ls, XSHIFT, rval)
+ if (sfd == NULL) {
+ call rg_lsetr (ls, SXSHIFT, rg_lstatr (ls, XSHIFT))
+ call rg_lsetr (ls, SYSHIFT, rg_lstatr (ls, YSHIFT))
+ }
+ newref = YES; newimage = YES; newfit = YES; newavg = YES
+ }
+
+ case LSCMD_YSHIFT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_YSHIFT)
+ call pargr (rg_lstatr (ls, YSHIFT))
+ } else {
+ call rg_lsetr (ls, YSHIFT, rval)
+ if (sfd == NULL) {
+ call rg_lsetr (ls, SXSHIFT, rg_lstatr (ls, XSHIFT))
+ call rg_lsetr (ls, SYSHIFT, rg_lstatr (ls, YSHIFT))
+ }
+ newref = YES; newimage = YES; newfit = YES; newavg = YES
+ }
+
+ case LSCMD_DNX:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_DNX)
+ call pargi (rg_lstati (ls, DNX))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_lseti (ls, DNX, ival)
+ newref = YES; newimage = YES; newfit = YES; newavg = YES
+ }
+
+ case LSCMD_DNY:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_DNY)
+ call pargi (rg_lstati (ls, DNY))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_lseti (ls, DNY, ival)
+ newref = YES; newimage = YES; newfit = YES; newavg = YES
+ }
+
+ case LSCMD_MAXNREGIONS:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_MAXNREGIONS)
+ call pargi (rg_lstati (ls, MAXNREGIONS))
+ }
+
+ case LSCMD_DATAMIN:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_DATAMIN)
+ call pargr (rg_lstatr (ls, DATAMIN))
+ } else {
+ call rg_lsetr (ls, DATAMIN, rval)
+ if (rg_lstati(ls,BSALGORITHM) != LS_PHOTOMETRY &&
+ rg_lstati(ls,BZALGORITHM) != LS_PHOTOMETRY)
+ newfit = YES; newavg = YES
+ }
+
+ case LSCMD_DATAMAX:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_DATAMAX)
+ call pargr (rg_lstatr (ls, DATAMAX))
+ } else {
+ call rg_lsetr (ls, DATAMAX, rval)
+ if (rg_lstati(ls,BSALGORITHM) != LS_PHOTOMETRY &&
+ rg_lstati(ls,BZALGORITHM) != LS_PHOTOMETRY)
+ newfit = YES; newavg = YES
+ }
+
+ case LSCMD_MAXITER:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_MAXITER)
+ call pargi (rg_lstati (ls, MAXITER))
+ } else {
+ call rg_lseti (ls, MAXITER, ival)
+ if (rg_lstati(ls,BSALGORITHM) != LS_PHOTOMETRY &&
+ rg_lstati(ls,BZALGORITHM) != LS_PHOTOMETRY) {
+ if (rg_lstati(ls,BSALGORITHM) == LS_FIT &&
+ rg_lstati(ls,BZALGORITHM) == LS_FIT) {
+ newfit = YES; newavg = YES
+ } else
+ newavg = YES
+ }
+ }
+
+ case LSCMD_NREJECT:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_NREJECT)
+ call pargi (rg_lstati (ls, NREJECT))
+ } else {
+ call rg_lseti (ls, NREJECT, ival)
+ newfit = YES; newavg = YES
+ if (rg_lstati(ls,BSALGORITHM) == LS_FIT ||
+ rg_lstati(ls,BZALGORITHM) == LS_FIT)
+ newfit = YES
+ newavg = YES
+ }
+
+ case LSCMD_LOREJECT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_LOREJECT)
+ call pargr (rg_lstatr (ls, LOREJECT))
+ } else {
+ call rg_lsetr (ls, LOREJECT, rval)
+ if (rg_lstati(ls,BSALGORITHM) == LS_FIT ||
+ rg_lstati(ls,BZALGORITHM) == LS_FIT)
+ newfit = YES
+ newavg = YES
+ }
+
+ case LSCMD_HIREJECT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_HIREJECT)
+ call pargr (rg_lstatr (ls, HIREJECT))
+ } else {
+ call rg_lsetr (ls, HIREJECT, rval)
+ if (rg_lstati(ls,BSALGORITHM) == LS_FIT ||
+ rg_lstati(ls,BZALGORITHM) == LS_FIT)
+ newfit = YES
+ newavg = YES
+ }
+
+ case LSCMD_GAIN:
+ call gargstr (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_lstats (ls, CCDGAIN, Memc[str], SZ_LINE)
+ call printf ("%s: %s\n")
+ call pargstr (KY_GAIN)
+ call pargstr (Memc[str])
+ } else {
+ call rg_lsets (ls, CCDGAIN, Memc[cmd])
+ if (imr != NULL) {
+ call rg_lgain (imr, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,GAIN)))
+ call rg_lsetr (ls, RGAIN, rg_lstatr(ls,GAIN))
+ }
+ if (im1 != NULL) {
+ call rg_lgain (im1, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,GAIN)))
+ call rg_lsetr (ls, IGAIN, rg_lstatr(ls,GAIN))
+ }
+ newfit = YES; newavg = YES
+ }
+
+ case LSCMD_READNOISE:
+ call gargstr (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_lstats (ls, CCDREAD, Memc[str], SZ_LINE)
+ call printf ("%s: %s\n")
+ call pargstr (KY_READNOISE)
+ call pargstr (Memc[str])
+ } else {
+ call rg_lsets (ls, CCDREAD, Memc[cmd])
+ if (imr != NULL) {
+ call rg_lrdnoise (imr, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,READNOISE)))
+ call rg_lsetr (ls, RREADNOISE, rg_lstatr(ls,READNOISE))
+ }
+ if (im1 != NULL) {
+ call rg_lrdnoise (im1, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,READNOISE)))
+ call rg_lsetr (ls, IREADNOISE, rg_lstatr(ls,READNOISE))
+ }
+ newfit = YES; newavg = YES
+ }
+
+ case LSCMD_SHOW:
+ call gdeactivate (gd, 0)
+ call rg_lshow (ls)
+ call greactivate (gd, 0)
+
+ case LSCMD_MARKCOORDS, LSCMD_MARKSECTIONS:
+ call gdeactivate (gd, 0)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ fd = NULL
+ } else if (access (Memc[cmd], 0, 0) == YES) {
+ call printf ("Warning: file %s already exists\n")
+ call pargstr (Memc[cmd])
+ fd = NULL
+ } else {
+ fd = open (Memc[cmd], NEW_FILE, TEXT_FILE)
+ }
+ call printf ("\n")
+ if (imr == NULL || im1 == NULL) {
+ call printf ("The reference or input image is undefined.\n")
+ } else {
+ if (reglist != NULL) {
+ call fntclsb (reglist)
+ reglist = NULL
+ }
+ if (ncmd == LSCMD_MARKCOORDS) {
+ nref = rg_lmkxy (fd, imr, ls, 1, rg_lstati (ls,
+ MAXNREGIONS))
+ } else {
+ nref = rg_lmkregions (fd, imr, ls, 1, rg_lstati (ls,
+ MAXNREGIONS), Memc[str], SZ_LINE)
+ }
+ if (nref <= 0) {
+ call rg_lstats (ls, REGIONS, Memc[str], SZ_LINE)
+ iferr (reglist = fntopnb (Memc[str], NO))
+ reglist = NULL
+ if (rg_lregions (reglist, imr, ls, 1, 1) > 0)
+ ;
+ call rg_lsets (ls, REGIONS, Memc[str])
+ call rg_lseti (ls, CNREGION, 1)
+ } else {
+ call rg_lseti (ls, CNREGION, 1)
+ call rg_lsets (ls, REGIONS, Memc[str])
+ newref = YES; newimage = YES
+ newfit = YES; newavg = YES
+ }
+ }
+ call printf ("\n")
+ if (fd != NULL)
+ call close (fd)
+ call greactivate (gd, 0)
+
+ default:
+ call printf ("Unknown or ambiguous colon command\7\n")
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/linmatch/rgldbio.x b/pkg/images/immatch/src/linmatch/rgldbio.x
new file mode 100644
index 00000000..63876985
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rgldbio.x
@@ -0,0 +1,225 @@
+include "linmatch.h"
+
+# RG_LWREC -- Procedure to write out the entire record.
+
+procedure rg_lwrec (db, dformat, ls)
+
+pointer db #I pointer to the database file
+int dformat #I is the scaling file in database format
+pointer ls #I pointer to the linmatch structure
+
+pointer sp, image
+real rg_lstatr()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+
+ if (dformat == YES) {
+ call rg_ldbparams (db, ls)
+ call rg_lwreg (db, ls)
+ call rg_ldbtscale (db, ls)
+ } else {
+ call rg_lstats (ls, IMAGE, Memc[image], SZ_FNAME)
+ call fprintf (db, "%s %g %g %g %g")
+ call pargstr (Memc[image])
+ call pargr (rg_lstatr(ls, TBSCALE))
+ call pargr (rg_lstatr(ls, TBZERO))
+ call pargr (rg_lstatr(ls, TBSCALEERR))
+ call pargr (rg_lstatr(ls, TBZEROERR))
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_LWREG -- Write out the results for each region.
+
+procedure rg_lwreg (db, ls)
+
+pointer db #I pointer to the database file
+pointer ls #I pointer to the intensity matching structure
+
+int i, nregions, rc1, rc2, rl1, rl2, c1, c2, l1, l2, del
+real xshift, yshift, bscale, bzero, bserr, bzerr
+int rg_lstati()
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ xshift = rg_lstatr (ls, SXSHIFT)
+ yshift = rg_lstatr (ls, SYSHIFT)
+
+ nregions = rg_lstati (ls, NREGIONS)
+ do i = 1, nregions {
+
+ rc1 = Memi[rg_lstatp (ls, RC1)+i-1]
+ rc2 = Memi[rg_lstatp (ls, RC2)+i-1]
+ rl1 = Memi[rg_lstatp (ls, RL1)+i-1]
+ rl2 = Memi[rg_lstatp (ls, RL2)+i-1]
+ if (IS_INDEFI(rc1))
+ c1 = INDEFI
+ else
+ c1 = rc1 + xshift
+ if (IS_INDEFI(rc2))
+ c2 = INDEFI
+ else
+ c2 = rc2 + xshift
+ if (IS_INDEFI(rl1))
+ l1 = INDEFI
+ else
+ l1 = rl1 + yshift
+ if (IS_INDEFI(rl2))
+ l2 = INDEFI
+ else
+ l2 = rl2 + yshift
+
+ bscale = Memr[rg_lstatp(ls,RBSCALE)+i-1]
+ bzero = Memr[rg_lstatp(ls,RBZERO)+i-1]
+ bserr = Memr[rg_lstatp(ls,RBSCALEERR)+i-1]
+ bzerr = Memr[rg_lstatp(ls,RBZEROERR)+i-1]
+ del = Memi[rg_lstatp(ls,RDELETE)+i-1]
+
+ call rg_ldbscaler (db, rc1, rc2, rl1, rl2, c1, c2, l1, l2,
+ bscale, bzero, bserr, bzerr, del)
+ }
+end
+
+
+# RG_LDBPARAMS -- Write the intensity matching parameters to the database file.
+
+procedure rg_ldbparams (db, ls)
+
+pointer db #I pointer to the database file
+pointer ls #I pointer to the intensity matching structure
+
+pointer sp, str
+int rg_lstati()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Write out the time record was written.
+ call dtput (db, "\n")
+ call dtptime (db)
+
+ # Write out the record name.
+ call rg_lstats (ls, RECORD, Memc[str], SZ_FNAME)
+ call dtput (db, "begin\t%s\n")
+ call pargstr (Memc[str])
+
+ # Write the image names.
+ call rg_lstats (ls, IMAGE, Memc[str], SZ_FNAME)
+ call dtput (db, "\t%s\t\t%s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ call rg_lstats (ls, REFIMAGE, Memc[str], SZ_FNAME)
+ call dtput (db, "\t%s\t%s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str])
+
+ call dtput (db, "\t%s\t%d\n")
+ call pargstr (KY_NREGIONS)
+ call pargi (rg_lstati(ls, NREGIONS))
+
+ call sfree (sp)
+end
+
+
+# RG_LDBSCALER -- Write the scaling parameters for each region
+
+procedure rg_ldbscaler (db, rc1, rc2, rl1, rl2, c1, c2, l1, l2, bscale,
+ bzero, bserr, bzerr, del)
+
+pointer db # pointer to the database file
+int rc1, rc2 # reference image region column limits
+int rl1, rl2 # reference image region line limits
+int c1, c2 # image region column limits
+int l1, l2 # image region line limits
+real bscale # the scaling parameter
+real bzero # the offset parameter
+real bserr # the error in the scaling parameter
+real bzerr # the error in the offset parameter
+int del # the deletions index
+
+begin
+ if (IS_INDEFI(rc1) || IS_INDEFI(c1)) {
+ call dtput (db,"\t[INDEF] [INDEF] %g %g %g %g %s\n")
+ } else {
+ call dtput (db,"\t[%d:%d,%d:%d] [%d:%d,%d:%d] %g %g %g %g %s\n")
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (rl1)
+ call pargi (rl2)
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (l1)
+ call pargi (l2)
+ }
+
+ call pargr (bscale)
+ call pargr (bzero)
+ call pargr (bserr)
+ call pargr (bzerr)
+ if (del == NO)
+ call pargstr ("")
+ else
+ call pargstr ("[Rejected/Deleted]")
+end
+
+
+# RG_LDBTSCALE -- Write the final scaling parameters and their errors.
+
+procedure rg_ldbtscale (db, ls)
+
+pointer db #I pointer to the text database file
+pointer ls #I pointer to the linmatch structure
+
+real rg_lstatr()
+
+begin
+ call dtput (db, "\tbscale\t\t%g\n")
+ call pargr (rg_lstatr(ls, TBSCALE))
+ call dtput (db, "\tbzero\t\t%g\n")
+ call pargr (rg_lstatr (ls, TBZERO))
+ call dtput (db, "\tbserr\t\t%g\n")
+ call pargr (rg_lstatr (ls, TBSCALEERR))
+ call dtput (db, "\tbzerr\t\t%g\n")
+ call pargr (rg_lstatr (ls, TBZEROERR))
+end
+
+
+# RG_LPWREC -- Print the computed scaling factors for the region.
+
+procedure rg_lpwrec (ls, i)
+
+pointer ls #I pointer to the linmatch structure
+int i #I the current region
+
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ if (i == 0) {
+ call printf (
+ "Results: bscale = %g +/- %g bzero = %g +/- %g\n")
+ call pargr (rg_lstatr (ls, TBSCALE))
+ call pargr (rg_lstatr (ls, TBSCALEERR))
+ call pargr (rg_lstatr (ls, TBZERO))
+ call pargr (rg_lstatr (ls, TBZEROERR))
+ } else {
+ call printf (
+ "Region %d: [%d:%d,%d:%d] bscale = %g +/- %g bzero = %g +/- %g\n")
+ call pargi (i)
+ call pargi (Memi[rg_lstatp(ls,RC1)+i-1])
+ call pargi (Memi[rg_lstatp(ls,RC2)+i-1])
+ call pargi (Memi[rg_lstatp(ls,RL1)+i-1])
+ call pargi (Memi[rg_lstatp(ls,RL2)+i-1])
+ call pargr (Memr[rg_lstatp(ls,RBSCALE)+i-1])
+ call pargr (Memr[rg_lstatp(ls,RBSCALEERR)+i-1])
+ call pargr (Memr[rg_lstatp(ls,RBZERO)+i-1])
+ call pargr (Memr[rg_lstatp(ls,RBZEROERR)+i-1])
+ }
+end
diff --git a/pkg/images/immatch/src/linmatch/rgldelete.x b/pkg/images/immatch/src/linmatch/rgldelete.x
new file mode 100644
index 00000000..2e16923a
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rgldelete.x
@@ -0,0 +1,993 @@
+include <gset.h>
+include <mach.h>
+include "linmatch.h"
+
+# RG_LFIND -- Find the point nearest the cursor regardless of whether it
+# has been deleted or not.
+
+int procedure rg_lfind (gd, ls, wcs, wx, wy, bscale, bzero, plot_type)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int wcs #I the wcs of the point
+real wx #I the x coordinate of point to be deleted
+real wy #I the y coordinate of point to be deleted
+real bscale #I the computed bscale value
+real bzero #I the computed bzero value
+int plot_type #I the current plot type
+
+int region
+int rg_mmffind(), rg_mmrfind(), rg_bzffind(), rg_bzrfind()
+int rg_msffind(), rg_msrfind()
+
+begin
+ switch (plot_type) {
+ case LS_MMFIT:
+ region = rg_mmffind (gd, ls, wx, wy)
+ case LS_MMRESID:
+ region = rg_mmrfind (gd, ls, wx, wy, bscale, bzero)
+ case LS_BSZFIT:
+ region = rg_bzffind (gd, ls, wcs, wx, wy)
+ case LS_BSZRESID:
+ region = rg_bzrfind (gd, ls, wcs, wx, wy, bscale, bzero)
+ case LS_MAGSKYFIT:
+ region = rg_msffind (gd, ls, wcs, wx, wy)
+ case LS_MAGSKYRESID:
+ region = rg_msrfind (gd, ls, wcs, wx, wy, bscale, bzero)
+ default:
+ region = 0
+ }
+
+ return (region)
+end
+
+
+# RG_LDELETE -- Delete or undelete regions from the data.
+
+int procedure rg_ldelete (gd, ls, udelete, wcs, wx, wy, bscale, bzero,
+ plot_type, delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+int wcs #I the wcs for multi-wcs plots
+real wx #I the x coordinate of point to be deleted
+real wy #I the y coordinate of point to be deleted
+real bscale #I the computed bscale value
+real bzero #I the computed bzero value
+int plot_type #I the current plot type
+int delete #I delete the point
+
+int region
+int rg_rdelete(), rg_mmfdelete(), rg_mmrdelete(), rg_bzfdelete()
+int rg_bzrdelete(), rg_msfdelete(), rg_msrdelete()
+
+begin
+ switch (plot_type) {
+ case LS_MMHIST:
+ region = rg_rdelete (gd, ls, udelete, delete)
+ case LS_MMFIT:
+ region = rg_mmfdelete (gd, ls, udelete, wx, wy, delete)
+ case LS_MMRESID:
+ region = rg_mmrdelete (gd, ls, udelete, wx, wy, bscale,
+ bzero, delete)
+ case LS_RIFIT:
+ region = rg_rdelete (gd, ls, udelete, delete)
+ case LS_RIRESID:
+ region = rg_rdelete (gd, ls, udelete, delete)
+ case LS_BSZFIT:
+ region = rg_bzfdelete (gd, ls, udelete, wcs, wx, wy, delete)
+ case LS_BSZRESID:
+ region = rg_bzrdelete (gd, ls, udelete, wcs, wx, wy, bscale,
+ bzero, delete)
+ case LS_MAGSKYFIT:
+ region = rg_msfdelete (gd, ls, udelete, wcs, wx, wy, delete)
+ case LS_MAGSKYRESID:
+ region = rg_msrdelete (gd, ls, udelete, wcs, wx, wy, bscale,
+ bzero, delete)
+ default:
+ region = 0
+ }
+
+ return (region)
+end
+
+
+# RG_RDELETE -- Delete or undelete a particular region from the data using
+# a histogram or fit plot.
+
+int procedure rg_rdelete (gd, ls, udelete, delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+int delete #I delete the point
+
+int region
+int rg_lstati()
+pointer rg_lstatp()
+
+begin
+ # Get the current region.
+ region = rg_lstati (ls, CNREGION)
+ if (region < 1 || region > rg_lstati (ls, NREGIONS))
+ return (0)
+
+ # Delete or undelete the region.
+ if (delete == YES) {
+ if (Memi[rg_lstatp(ls,RDELETE)+region-1] == LS_NO) {
+ udelete[region] = YES
+ return (region)
+ } else
+ return (0)
+ } else {
+ if (Memi[rg_lstatp(ls,RDELETE)+region-1] != LS_NO) {
+ udelete[region] = NO
+ return (region)
+ } else
+ return (0)
+ }
+end
+
+
+# RG_MMFDELETE -- Delete or undelete a point computed from the mean, median,
+# or mode.
+
+int procedure rg_mmfdelete (gd, ls, udelete, wx, wy, delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+int delete #I delete the input object
+
+int nregions, region, mtype
+pointer sp, xdata, ydata
+int rg_lstati(), rg_lpdelete(), rg_lpundelete()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ # Determine the type of data to plot.
+ mtype = 0
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ if (mtype <= 0)
+ return (0)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (xdata, nregions, TY_REAL)
+ call salloc (ydata, nregions, TY_REAL)
+
+ # Get the data.
+ switch (mtype) {
+ case LS_MEAN:
+ call amovr (Memr[rg_lstatp(ls,IMEAN)], Memr[xdata], nregions)
+ call amovr (Memr[rg_lstatp(ls,RMEAN)], Memr[ydata], nregions)
+ case LS_MEDIAN:
+ call amovr (Memr[rg_lstatp(ls,IMEDIAN)], Memr[xdata], nregions)
+ call amovr (Memr[rg_lstatp(ls,RMEDIAN)], Memr[ydata], nregions)
+ case LS_MODE:
+ call amovr (Memr[rg_lstatp(ls,IMODE)], Memr[xdata], nregions)
+ call amovr (Memr[rg_lstatp(ls,RMODE)], Memr[ydata], nregions)
+ }
+
+ # Delete or undelete the point.
+ if (delete == YES)
+ region = rg_lpdelete (gd, 1, wx, wy, Memr[xdata], Memr[ydata],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+ else
+ region = rg_lpundelete (gd, 1, wx, wy, Memr[xdata], Memr[ydata],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_MMRDELETE -- Delete or undelete a point computed from the mean, median,
+# or mode residuals plots.
+
+int procedure rg_mmrdelete (gd, ls, udelete, wx, wy, bscale, bzero, delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+real bscale #I the computed bscale factor
+real bzero #I the computed bzero factor
+int delete #I delete the input object
+
+int nregions, region, mtype
+pointer sp, xdata, ydata
+int rg_lstati(), rg_lpdelete(), rg_lpundelete()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ # Determine the type of data to plot.
+ mtype = 0
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ if (mtype <= 0)
+ return (0)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (xdata, nregions, TY_REAL)
+ call salloc (ydata, nregions, TY_REAL)
+
+ switch (mtype) {
+ case LS_MEAN:
+ call amovr (Memr[rg_lstatp(ls,IMEAN)], Memr[xdata], nregions)
+ call altmr (Memr[rg_lstatp(ls,IMEAN)], Memr[ydata], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMEAN)], Memr[ydata], Memr[ydata],
+ nregions)
+ case LS_MEDIAN:
+ call amovr (Memr[rg_lstatp(ls,IMEDIAN)], Memr[xdata], nregions)
+ call altmr (Memr[rg_lstatp(ls,IMEDIAN)], Memr[ydata], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMEDIAN)], Memr[ydata], Memr[ydata],
+ nregions)
+ case LS_MODE:
+ call amovr (Memr[rg_lstatp(ls,IMODE)], Memr[xdata], nregions)
+ call altmr (Memr[rg_lstatp(ls,IMODE)], Memr[ydata], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMODE)], Memr[ydata], Memr[ydata],
+ nregions)
+ }
+
+ # Delete or undelete the point.
+ if (delete == YES)
+ region = rg_lpdelete (gd, 1, wx, wy, Memr[xdata], Memr[ydata],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+ else
+ region = rg_lpundelete (gd, 1, wx, wy, Memr[xdata], Memr[ydata],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_BZFDELETE -- Delete or undelete a point computed from the average
+# of the fitted bscale or bzeros.
+
+int procedure rg_bzfdelete (gd, ls, udelete, wcs, wx, wy, delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+int wcs #I the wcs number
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+int delete #I delete the input object
+
+int i, nregions, region
+pointer sp, xreg
+int rg_lstati(), rg_lpdelete(), rg_lpundelete()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ call smark (sp)
+ call salloc (xreg, nregions, TY_REAL)
+ do i = 1, nregions
+ Memr[xreg+i-1] = i
+
+ # Delete or undelete the point.
+ if (delete == YES) {
+ if (wcs == 1)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[xreg],
+ Memr[rg_lstatp(ls,RBSCALE)], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else if (wcs == 2)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[xreg],
+ Memr[rg_lstatp(ls,RBZERO)], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else
+ region = 0
+ } else {
+ if (wcs == 1)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[xreg],
+ Memr[rg_lstatp(ls,RBSCALE)], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else if (wcs == 2)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[xreg],
+ Memr[rg_lstatp(ls,RBZERO)], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else
+ region = 0
+ }
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_BZRDELETE -- Delete or undelete a point computed from the average
+# of the fitted bscale or bzero residuals.
+
+int procedure rg_bzrdelete (gd, ls, udelete, wcs, wx, wy, bscale, bzero,
+ delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+int wcs #I the wcs number
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+real bscale #I the input bscale value
+real bzero #I the input bzero value
+int delete #I delete the input object
+
+int i, nregions, region
+pointer sp, xreg, yreg
+int rg_lstati(), rg_lpdelete(), rg_lpundelete()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ call smark (sp)
+ call salloc (xreg, nregions, TY_REAL)
+ call salloc (yreg, nregions, TY_REAL)
+ do i = 1, nregions
+ Memr[xreg+i-1] = i
+
+ # Delete or undelete the point.
+ if (delete == YES) {
+ if (wcs == 1) {
+ call asubkr (Memr[rg_lstatp(ls,RBSCALE)], bscale, Memr[yreg],
+ nregions)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[xreg], Memr[yreg],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+ } else if (wcs == 2) {
+ call asubkr (Memr[rg_lstatp(ls,RBZERO)], bzero, Memr[yreg],
+ nregions)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[xreg],
+ Memr[yreg], Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+ } else
+ region = 0
+ } else {
+ if (wcs == 1) {
+ call asubkr (Memr[rg_lstatp(ls,RBSCALE)], bscale, Memr[yreg],
+ nregions)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[xreg],
+ Memr[yreg], Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+ } else if (wcs == 2) {
+ call asubkr (Memr[rg_lstatp(ls,RBZERO)], bzero, Memr[yreg],
+ nregions)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[xreg],
+ Memr[yreg], Memi[rg_lstatp(ls,RDELETE)], udelete, nregions)
+ } else
+ region = 0
+ }
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_MSFDELETE -- Delete or undelete a point computed from the average
+# of the fitted bscale or bzeros.
+
+int procedure rg_msfdelete (gd, ls, udelete, wcs, wx, wy, delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+int wcs #I the wcs number
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+int delete #I delete the input object
+
+int nregions, region
+int rg_lstati(), rg_lpdelete(), rg_lpundelete()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ # Delete or undelete the point.
+ if (delete == YES) {
+ if (wcs == 1)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ IMAG)], Memr[rg_lstatp(ls,RMAG)], Memi[rg_lstatp(ls,
+ RDELETE)], udelete, nregions)
+ else if (wcs == 2)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ ISKY)], Memr[rg_lstatp(ls,RSKY)], Memi[rg_lstatp(ls,
+ RDELETE)], udelete, nregions)
+ else
+ region = 0
+ } else {
+ if (wcs == 1)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ IMAG)], Memr[rg_lstatp(ls,RMAG)], Memi[rg_lstatp(ls,
+ RDELETE)], udelete, nregions)
+ else if (wcs == 2)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ ISKY)], Memr[rg_lstatp(ls,RSKY)], Memi[rg_lstatp(ls,
+ RDELETE)], udelete, nregions)
+ else
+ region = 0
+ }
+
+ return (region)
+end
+
+
+# RG_MSRDELETE -- Delete or undelete a point computed from the average
+# of the fitted bscale or bzeros.
+
+int procedure rg_msrdelete (gd, ls, udelete, wcs, wx, wy, bscale, bzero, delete)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I/O the user deletions array
+int wcs #I the wcs number
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+real bscale #I the input bscale value
+real bzero #I the input bzero value
+int delete #I delete the input object
+
+int nregions, region
+pointer sp, resid
+int rg_lstati(), rg_lpdelete(), rg_lpundelete()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ call smark (sp)
+ call salloc (resid, nregions, TY_REAL)
+
+ if (wcs == 1) {
+ if (bscale > 0.0) {
+ call aaddkr (Memr[rg_lstatp(ls,IMAG)], -2.5*log10(bscale),
+ Memr[resid], nregions)
+ call asubr (Memr[rg_lstatp(ls,RMAG)], Memr[resid],
+ Memr[resid], nregions)
+ } else
+ call asubr (Memr[rg_lstatp(ls,RMAG)], Memr[rg_lstatp(ls,
+ IMAG)], Memr[resid], nregions)
+ } else {
+ call altmr (Memr[rg_lstatp(ls,ISKY)], Memr[resid], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RSKY)], Memr[resid], Memr[resid],
+ nregions)
+ }
+
+ # Delete or undelete the point.
+ if (delete == YES) {
+ if (wcs == 1)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ IMAG)], Memr[resid], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else if (wcs == 2)
+ region = rg_lpdelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ ISKY)], Memr[resid], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else
+ region = 0
+ } else {
+ if (wcs == 1)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ IMAG)], Memr[resid], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else if (wcs == 2)
+ region = rg_lpundelete (gd, wcs, wx, wy, Memr[rg_lstatp(ls,
+ ISKY)], Memr[resid], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions)
+ else
+ region = 0
+ }
+
+ call sfree (sp)
+
+ return (region)
+end
+
+# RG_MMFFIND -- Find a point computed from the mean, median, or mode.
+
+int procedure rg_mmffind (gd, ls, wx, wy)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+
+int nregions, mtype, region
+pointer sp, xdata, ydata
+int rg_lstati(), rg_lpfind()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ # Determine the type of data to plot.
+ mtype = 0
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ if (mtype <= 0)
+ return (0)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (xdata, nregions, TY_REAL)
+ call salloc (ydata, nregions, TY_REAL)
+
+ # Get the data.
+ switch (mtype) {
+ case LS_MEAN:
+ call amovr (Memr[rg_lstatp(ls,IMEAN)], Memr[xdata], nregions)
+ call amovr (Memr[rg_lstatp(ls,RMEAN)], Memr[ydata], nregions)
+ case LS_MEDIAN:
+ call amovr (Memr[rg_lstatp(ls,IMEDIAN)], Memr[xdata], nregions)
+ call amovr (Memr[rg_lstatp(ls,RMEDIAN)], Memr[ydata], nregions)
+ case LS_MODE:
+ call amovr (Memr[rg_lstatp(ls,IMODE)], Memr[xdata], nregions)
+ call amovr (Memr[rg_lstatp(ls,RMODE)], Memr[ydata], nregions)
+ }
+
+ region = rg_lpfind (gd, 1, wx, wy, Memr[xdata], Memr[ydata], nregions)
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_MMRFIND -- Find a point computed from the mean, median, or mode.
+
+int procedure rg_mmrfind (gd, ls, wx, wy, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+real bscale #I the input bscale factor
+real bzero #I the input bzero factor
+
+int nregions, mtype, region
+pointer sp, xdata, ydata
+int rg_lstati(), rg_lpfind()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ # Determine the type of data to plot.
+ mtype = 0
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ if (mtype <= 0)
+ return (0)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (xdata, nregions, TY_REAL)
+ call salloc (ydata, nregions, TY_REAL)
+
+ switch (mtype) {
+ case LS_MEAN:
+ call amovr (Memr[rg_lstatp(ls,IMEAN)], Memr[xdata], nregions)
+ call altmr (Memr[rg_lstatp(ls,IMEAN)], Memr[ydata], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMEAN)], Memr[ydata], Memr[ydata],
+ nregions)
+ case LS_MEDIAN:
+ call amovr (Memr[rg_lstatp(ls,IMEDIAN)], Memr[xdata], nregions)
+ call altmr (Memr[rg_lstatp(ls,IMEDIAN)], Memr[ydata], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMEDIAN)], Memr[ydata], Memr[ydata],
+ nregions)
+ case LS_MODE:
+ call amovr (Memr[rg_lstatp(ls,IMODE)], Memr[xdata], nregions)
+ call altmr (Memr[rg_lstatp(ls,IMODE)], Memr[ydata], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMODE)], Memr[ydata], Memr[ydata],
+ nregions)
+ }
+
+ region = rg_lpfind (gd, 1, wx, wy, Memr[xdata], Memr[ydata], nregions)
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_BZFFIND -- Find a point computed from the bscale and bzero fits
+# to all the regions.
+
+int procedure rg_bzffind (gd, ls, wcs, wx, wy)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int wcs #I the input wcs
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+
+int i, nregions, region
+pointer sp, xreg
+int rg_lstati(), rg_lpfind()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ call smark (sp)
+ call salloc (xreg, nregions, TY_REAL)
+ do i = 1, nregions
+ Memr[xreg+i-1] = i
+
+ if (wcs == 1)
+ region = rg_lpfind (gd, 1, wx, wy, Memr[xreg], Memr[rg_lstatp(ls,
+ RBSCALE)], nregions)
+ else if (wcs == 2)
+ region = rg_lpfind (gd, 2, wx, wy, Memr[xreg], Memr[rg_lstatp(ls,
+ RBZERO)], nregions)
+ else
+ region = 0
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_BZRFIND -- Find a point computed from the bscale and bzero fit
+# residuals to all the regions.
+
+int procedure rg_bzrfind (gd, ls, wcs, wx, wy, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int wcs #I the input wcs
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+real bscale #I the input bscale value
+real bzero #I the input bscale value
+
+int i, nregions, region
+pointer sp, xreg, yreg
+int rg_lstati(), rg_lpfind()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ call smark (sp)
+ call salloc (xreg, nregions, TY_REAL)
+ call salloc (yreg, nregions, TY_REAL)
+
+ do i = 1, nregions
+ Memr[xreg+i-1] = i
+
+ if (wcs == 1) {
+ call asubkr (Memr[rg_lstatp(ls,RBSCALE)], bscale, Memr[yreg],
+ nregions)
+ region = rg_lpfind (gd, 1, wx, wy, Memr[xreg], Memr[yreg],
+ nregions)
+ } else if (wcs == 2) {
+ call asubkr (Memr[rg_lstatp(ls,RBZERO)], bzero, Memr[yreg],
+ nregions)
+ region = rg_lpfind (gd, 2, wx, wy, Memr[xreg], Memr[yreg],
+ nregions)
+ } else
+ region = 0
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_MSFFIND -- Find a point computed from the bscale and bzero fits
+# to all the regions.
+
+int procedure rg_msffind (gd, ls, wcs, wx, wy)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int wcs #I the input wcs
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+
+int nregions, region
+int rg_lstati(), rg_lpfind()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ if (wcs == 1)
+ region = rg_lpfind (gd, 1, wx, wy, Memr[rg_lstatp(ls,IMAG)],
+ Memr[rg_lstatp(ls,RMAG)], nregions)
+ else if (wcs == 2)
+ region = rg_lpfind (gd, 2, wx, wy, Memr[rg_lstatp(ls,ISKY)],
+ Memr[rg_lstatp(ls,RSKY)], nregions)
+ else
+ region = 0
+
+ return (region)
+end
+
+
+# RG_MSRFIND -- Find a point computed from the bscale and bzero fits
+# to all the regions.
+
+int procedure rg_msrfind (gd, ls, wcs, wx, wy, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int wcs #I the input wcs
+real wx #I the input x coordinate
+real wy #I the input y coordinate
+real bscale #I the input bscale value
+real bzero #I the input bzero value
+
+int nregions, region
+pointer sp, resid
+int rg_lstati(), rg_lpfind()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (0)
+
+ call smark (sp)
+ call salloc (resid, nregions, TY_REAL)
+
+ if (wcs == 1) {
+ if (bscale > 0.0) {
+ call aaddkr (Memr[rg_lstatp(ls,IMAG)], -2.5*log10(bscale),
+ Memr[resid], nregions)
+ call asubr (Memr[rg_lstatp(ls,RMAG)], Memr[resid], Memr[resid],
+ nregions)
+ } else
+ call asubr (Memr[rg_lstatp(ls,RMAG)], Memr[rg_lstatp(ls,IMAG)],
+ Memr[resid], nregions)
+ region = rg_lpfind (gd, 1, wx, wy, Memr[rg_lstatp(ls,IMAG)],
+ Memr[resid], nregions)
+ } else if (wcs == 2) {
+ call altmr (Memr[rg_lstatp(ls,ISKY)], Memr[resid], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RSKY)], Memr[resid], Memr[resid],
+ nregions)
+ region = rg_lpfind (gd, 2, wx, wy, Memr[rg_lstatp(ls,ISKY)],
+ Memr[resid], nregions)
+ } else
+ region = 0
+
+ call sfree (sp)
+
+ return (region)
+end
+
+
+# RG_LPDELETE -- Delete a point from the plot.
+
+int procedure rg_lpdelete (gd, wcs, wx, wy, xdata, ydata, delete, udelete, npts)
+
+pointer gd #I the graphics stream descriptor
+int wcs #I the input wcs
+real wx, wy #I the point to be deleted.
+real xdata[ARB] #I the input x data array
+real ydata[ARB] #I the input y data array
+int delete[ARB] #I the deletions array
+int udelete[ARB] #I/O the user deletions array
+int npts #I the number of points
+
+int i, region
+real wx0, wy0, r2min, r2, x0, y0
+
+begin
+ call gctran (gd, wx, wy, wx0, wy0, wcs, 0)
+ r2min = MAX_REAL
+ region = 0
+
+ # Find the point to be deleted.
+ do i = 1, npts {
+ if (delete[i] != LS_NO)
+ next
+ call gctran (gd, xdata[i], ydata[i], x0, y0, wcs, 0)
+ r2 = (x0 - wx0) ** 2 + (y0 - wy0) ** 2
+ if (r2 < r2min) {
+ r2min = r2
+ region = i
+ }
+ }
+
+ if (region > 0) {
+ call gseti (gd, G_WCS, wcs)
+ call gscur (gd, xdata[region], ydata[region])
+ call gmark (gd, xdata[region], ydata[region], GM_CROSS, 2.0, 2.0)
+ udelete[region] = YES
+ }
+
+ return (region)
+end
+
+
+# RG_LPUNDELETE -- Undelete a point from the plot.
+
+int procedure rg_lpundelete (gd, wcs, wx, wy, xdata, ydata, delete,
+ udelete, npts)
+
+pointer gd #I the graphics stream descriptor
+int wcs #I the input wcs
+real wx, wy #I the point to be deleted.
+real xdata[ARB] #I the input x data array
+real ydata[ARB] #I the input y data array
+int delete[ARB] #I the deletions array
+int udelete[ARB] #I/O the user deletions array
+int npts #I the number of points
+
+int i, region
+real wx0, wy0, r2min, r2, x0, y0
+
+begin
+ call gctran (gd, wx, wy, wx0, wy0, wcs, 0)
+ r2min = MAX_REAL
+ region = 0
+
+ # Find the point to be deleted.
+ do i = 1, npts {
+ if (udelete[i] == NO)
+ next
+ call gctran (gd, xdata[i], ydata[i], x0, y0, wcs, 0)
+ r2 = (x0 - wx0) ** 2 + (y0 - wy0) ** 2
+ if (r2 < r2min) {
+ r2min = r2
+ region = i
+ }
+ }
+
+ if (region > 0) {
+ call gseti (gd, G_WCS, wcs)
+ call gscur (gd, xdata[region], ydata[region])
+ call gseti (gd, G_PMLTYPE, GL_CLEAR)
+ call gmark (gd, xdata[region], ydata[region], GM_CROSS, 2.0, 2.0)
+ call gseti (gd, G_PMLTYPE, GL_SOLID)
+ call gmark (gd, xdata[region], ydata[region], GM_BOX, 2.0, 2.0)
+ udelete[region] = NO
+ }
+
+ return (region)
+end
+
+
+# RG_LPFIND -- Find a point in the plot.
+
+int procedure rg_lpfind (gd, wcs, wx, wy, xdata, ydata, npts)
+
+pointer gd #I the graphics stream descriptor
+int wcs #I the input wcs
+real wx, wy #I the point to be deleted.
+real xdata[ARB] #I the input x data array
+real ydata[ARB] #I the input y data array
+int npts #I the number of points
+
+int i, region
+real wx0, wy0, r2min, x0, y0, r2
+
+begin
+ call gctran (gd, wx, wy, wx0, wy0, wcs, 0)
+ r2min = MAX_REAL
+ region = 0
+
+ # Find the point to be deleted.
+ do i = 1, npts {
+ call gctran (gd, xdata[i], ydata[i], x0, y0, wcs, 0)
+ r2 = (x0 - wx0) ** 2 + (y0 - wy0) ** 2
+ if (r2 < r2min) {
+ r2min = r2
+ region = i
+ }
+ }
+
+ return (region)
+end
+
diff --git a/pkg/images/immatch/src/linmatch/rgliscale.x b/pkg/images/immatch/src/linmatch/rgliscale.x
new file mode 100644
index 00000000..e760c7f8
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rgliscale.x
@@ -0,0 +1,593 @@
+include <gset.h>
+include <imhdr.h>
+include <ctype.h>
+include "linmatch.h"
+
+# Define the help files.
+define HELPFILE "immatch$src/linmatch/linmatch.key"
+
+# RG_LISCALE -- Scale the output image interactively.
+
+int procedure rg_liscale (imr, im1, im2, db, dformat, reglist, rpfd, ipfd, sfd,
+ ls, gd, id)
+
+pointer imr #I/O pointer to the reference image
+pointer im1 #I/O pointer to the input image
+pointer im2 #I/O pointer to the output image
+pointer db #I/O pointer to the database file
+int dformat #I is the scale file in database format
+pointer reglist #I/O the regions list descriptor
+int rpfd #I/O the reference photometry file descriptor
+int ipfd #I/O the input photometry file descriptor
+int sfd #I/O the shifts file descriptor
+pointer ls #I pointer to the linmatch structure
+pointer gd #I the graphics stream pointer
+pointer id #I display stream pointer
+
+int i, newref, newimage, newfit, newavg, newplot, plottype, wcs, key, reg
+int hplot, lplot, lplot_type
+pointer sp, cmd, udelete, stat
+real bscale, bzero, bserr, bzerr, wx, wy
+int rg_lstati(), rg_lplot(), clgcur(), rg_lgqverify(), rg_lgtverify()
+int rg_ldelete(), rg_lfind(), rg_mmhplot(), rg_rifplot(), rg_rirplot()
+int rg_lregions()
+pointer rg_lstatp()
+
+begin
+ call gdeactivate (gd, 0)
+
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+ call salloc (udelete, rg_lstati(ls, MAXNREGIONS), TY_INT)
+
+ # Initialize the fitting.
+ newref = YES
+ newimage = YES
+ newfit = YES
+ newavg = YES
+
+ # Initialize the plotting.
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN, LS_MEDIAN, LS_MODE:
+ if (rg_lstati (ls, NREGIONS) > 1)
+ plottype = LS_MMFIT
+ else
+ plottype = LS_MMHIST
+ case LS_FIT:
+ if (rg_lstati (ls, NREGIONS) > 1)
+ plottype = LS_BSZFIT
+ else
+ plottype = LS_RIFIT
+ case LS_PHOTOMETRY:
+ plottype = LS_BSZFIT
+ default:
+ }
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN, LS_MEDIAN, LS_MODE:
+ if (rg_lstati (ls, NREGIONS) > 1)
+ plottype = LS_MMFIT
+ else
+ plottype = LS_MMHIST
+ case LS_FIT:
+ if (rg_lstati (ls, NREGIONS) > 1)
+ plottype = LS_BSZFIT
+ else
+ plottype = LS_RIFIT
+ case LS_PHOTOMETRY:
+ plottype = LS_BSZFIT
+ default:
+ }
+
+ # Do the initial fit.
+ if (rg_lstati (ls, NREGIONS) <= 0) {
+ call gclear (gd)
+ call gflush (gd)
+ bscale = 1.0; bzero = 0.0
+ bserr = INDEFR; bzerr = INDEFR
+ call printf ("The regions/photometry list is empty\n")
+ } else {
+ call amovki (LS_NO, Memi[rg_lstatp(ls,RDELETE)], rg_lstati(ls,
+ NREGIONS))
+ call rg_scale (imr, im1, ls, bscale, bzero, bserr, bzerr, YES)
+ call amovki (NO, Memi[udelete], rg_lstati(ls,NREGIONS))
+ if (rg_lplot (gd, imr, im1, ls, Memi[udelete], 1, bscale, bzero,
+ plottype) == OK) {
+ newref = NO
+ newimage = NO
+ newfit = NO
+ newavg = NO
+ call rg_lpwrec (ls, 0)
+ } else {
+ call gclear (gd)
+ call gflush (gd)
+ call rg_lstats (ls, IMAGE, Memc[cmd], SZ_FNAME)
+ call printf ("Error computing scale factors for image %s\n")
+ call pargstr (Memc[cmd])
+ }
+ }
+ newplot = NO
+
+ # Loop over the cursor commands.
+ while (clgcur ("gcommands", wx, wy, wcs, key, Memc[cmd], SZ_LINE) !=
+ EOF) {
+
+ switch (key) {
+
+ # Print the help page.
+ case '?':
+ call gpagefile (gd, HELPFILE, "")
+
+ # Quit the task gracefully.
+ case 'q':
+ if (rg_lgqverify ("linmatch", db, dformat, ls,
+ key) == YES) {
+ call sfree (sp)
+ return (rg_lgtverify (key))
+ }
+
+ # Refit the data.
+ case 'f':
+ if (newref == YES || newimage == YES || newfit == YES ||
+ newavg == YES) {
+ if (rg_lstati(ls, BSALGORITHM) != LS_PHOTOMETRY &&
+ rg_lstati(ls, BZALGORITHM) != LS_PHOTOMETRY) {
+ if (newref == YES) {
+ if (rg_lregions (reglist, imr, ls, 1, YES) > 0)
+ ;
+ } else if (newimage == YES) {
+ call rg_lindefr (ls)
+ }
+ }
+ if (newfit == YES)
+ call amovki (LS_NO, Memi[rg_lstatp(ls,RDELETE)],
+ rg_lstati(ls,NREGIONS))
+ else if (newavg == YES) {
+ do i = 1, rg_lstati(ls,NREGIONS) {
+ if (Memi[rg_lstatp(ls,RDELETE)+i-1] ==
+ LS_DELETED || Memi[rg_lstatp(ls,
+ RDELETE)+i-1] == LS_BADSIGMA)
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_NO
+ }
+
+ }
+ do i = 1, rg_lstati(ls,NREGIONS) {
+ if (Memi[udelete+i-1] == YES)
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_DELETED
+ }
+ if (newfit == YES)
+ call rg_scale (imr, im1, ls, bscale, bzero, bserr,
+ bzerr, YES)
+ else if (newavg == YES)
+ call rg_scale (imr, im1, ls, bscale, bzero, bserr,
+ bzerr, NO)
+ newref = NO
+ newimage = NO
+ newfit = NO
+ newavg = NO
+ newplot = YES
+ }
+
+ # Plot the default graph.
+ case 'g':
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN, LS_MEDIAN, LS_MODE:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_MMFIT)
+ newplot = YES
+ plottype = LS_MMFIT
+ } else {
+ if (plottype != LS_MMHIST)
+ newplot = YES
+ plottype = LS_MMHIST
+ }
+ case LS_FIT:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_BSZFIT)
+ newplot = YES
+ plottype = LS_BSZFIT
+ } else {
+ if (plottype != LS_RIFIT)
+ newplot = YES
+ plottype = LS_RIFIT
+ }
+ case LS_PHOTOMETRY:
+ if (plottype != LS_BSZFIT)
+ newplot = YES
+ plottype = LS_BSZFIT
+ default:
+ }
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN, LS_MEDIAN, LS_MODE:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_MMFIT)
+ newplot = YES
+ plottype = LS_MMFIT
+ } else {
+ if (plottype != LS_MMHIST)
+ newplot = YES
+ plottype = LS_MMHIST
+ }
+ case LS_FIT:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_BSZFIT)
+ plottype = LS_BSZFIT
+ } else {
+ if (plottype != LS_RIFIT)
+ plottype = LS_RIFIT
+ }
+ case LS_PHOTOMETRY:
+ if (plottype != LS_BSZFIT)
+ newplot = YES
+ plottype = LS_BSZFIT
+ default:
+ }
+
+ # Graph the residuals from the current fit.
+ case 'i':
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN, LS_MEDIAN, LS_MODE:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_MMRESID)
+ newplot = YES
+ plottype = LS_MMRESID
+ } else {
+ call printf (
+ "There are too few regions for a residuals plot\n")
+ }
+ case LS_FIT:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_BSZRESID)
+ newplot = YES
+ plottype = LS_BSZRESID
+ } else {
+ if (plottype != LS_RIRESID)
+ newplot = YES
+ plottype = LS_RIRESID
+ }
+ case LS_PHOTOMETRY:
+ if (plottype == LS_BSZFIT) {
+ newplot = YES
+ plottype = LS_BSZRESID
+ } else if (plottype == LS_MAGSKYFIT) {
+ newplot = YES
+ plottype = LS_MAGSKYRESID
+ }
+ default:
+ }
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN, LS_MEDIAN, LS_MODE:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_MMRESID)
+ newplot = YES
+ plottype = LS_MMRESID
+ } else {
+ call printf (
+ "There are too few regions for a residuals plot\n")
+ }
+ case LS_FIT:
+ if (rg_lstati (ls, NREGIONS) > 1) {
+ if (plottype != LS_BSZRESID)
+ newplot = YES
+ plottype = LS_BSZRESID
+ } else {
+ if (plottype != LS_RIRESID)
+ newplot = YES
+ plottype = LS_RIRESID
+ }
+ case LS_PHOTOMETRY:
+ if (plottype == LS_BSZFIT) {
+ newplot = YES
+ plottype = LS_BSZRESID
+ } else if (plottype == LS_MAGSKYFIT) {
+ newplot = YES
+ plottype = LS_MAGSKYRESID
+ }
+ default:
+ }
+
+ # Plot the histogram and show the statistics of a given region.
+ # selected from a plot.
+ case 's':
+ if (imr != NULL && im1 != NULL) {
+ reg = rg_lfind (gd, ls, wcs, wx, wy, bscale, bzero,
+ plottype)
+ if (reg > 0) {
+ if (rg_mmhplot (gd, imr, im1, ls, Memi[udelete],
+ reg) == OK) {
+ call rg_lpwrec (ls, reg)
+ } else {
+ call printf (
+ "Unable to plot statistics for region %d\n")
+ call pargi (reg)
+ }
+ } else
+ call printf ("Unable to plot region statistics\n")
+ } else
+ call printf (
+ "The reference or input image is undefined\n")
+
+ # Trace the fit of a given region selected from a plot.
+ case 't':
+ if (imr != NULL && im1 != NULL && (rg_lstati(ls,
+ BSALGORITHM) == LS_FIT || rg_lstati(ls,BZALGORITHM) ==
+ LS_FIT)) {
+ reg = rg_lfind (gd, ls, wcs, wx, wy, bscale, bzero,
+ plottype)
+ if (reg > 0) {
+ if (plottype == LS_BSZFIT)
+ stat = rg_rifplot (gd, imr, im1, ls,
+ Memi[udelete], reg)
+ else if (plottype == LS_BSZRESID)
+ stat = rg_rirplot (gd, imr, im1, ls,
+ Memi[udelete], reg)
+ else
+ stat = ERR
+ if (stat == OK)
+ call rg_lpwrec (ls, reg)
+ else {
+ call printf (
+ "Unable to plot statistics for region %d\n")
+ call pargi (reg)
+ }
+ } else
+ call printf (
+ "Unable to plot region statistics\n")
+ } else
+ call printf (
+ "The least squares fit is undefined\n")
+
+ # Plot the statistics and show the histograms for each
+ # region in turn.
+ case 'h':
+ if (imr != NULL && im1 != NULL) {
+ reg = 1
+ if (rg_mmhplot (gd, imr, im1, ls, Memi[udelete],
+ reg) == ERR) {
+ call printf (
+ "Unable to plot statistics for region 1\n")
+ next
+ }
+ hplot = NO
+ call printf (
+ "Hit [spbar=next,-=prev,s=stats,?=help,q=quit]:")
+ while (clgcur ("gcommands", wx, wy, wcs, key, Memc[cmd],
+ SZ_LINE) != EOF) {
+ switch (key) {
+ case '?':
+ call printf (
+ "Hit [spbar=next,-=prev,s=stats,?=help,q=quit]:")
+ case 'q':
+ call printf ("\n")
+ break
+ case ' ':
+ if (reg < rg_lstati (ls, NREGIONS)) {
+ reg = reg + 1
+ hplot = YES
+ }
+ case '-':
+ if (reg > 1) {
+ reg = reg - 1
+ hplot = YES
+ }
+ case 's':
+ call rg_lpwrec (ls, reg)
+ }
+ if (hplot == YES) {
+ if (rg_mmhplot (gd, imr, im1, ls,
+ Memi[udelete], reg) == ERR)
+ ;
+ call printf (
+ "Hit [spbar=next,-=prev,s=stats,?=help,q=quit]:")
+ hplot = NO
+ }
+ }
+ newplot = YES
+ } else
+ call printf (
+ "The reference or input image is undefined\n")
+
+ # Step through the least sqares fits one at a time.
+ case 'l':
+ if (imr != NULL && im1 != NULL && (rg_lstati(ls,
+ BSALGORITHM) == LS_FIT || rg_lstati(ls,BZALGORITHM) ==
+ LS_FIT)) {
+ reg = 1
+ lplot = NO
+ if (plottype == LS_BSZFIT || plottype == LS_RIFIT)
+ lplot_type = LS_RIFIT
+ else if (plottype == LS_BSZRESID || plottype ==
+ LS_RIRESID)
+ lplot_type = LS_RIRESID
+ if (lplot_type == LS_RIFIT)
+ stat = rg_rifplot (gd, imr, im1, ls, Memi[udelete],
+ reg)
+ else if (lplot_type == LS_RIRESID)
+ stat = rg_rirplot (gd, imr, im1, ls, Memi[udelete],
+ reg)
+ else
+ stat = ERR
+ if (stat == ERR) {
+ call printf ("Unable to plot fits for region 1\n")
+ next
+ }
+ call printf (
+ "Hit [spbar=next,-=prev,l=fit,i=resid,s=stats,?=help,q=quit]:")
+ while (clgcur ("gcommands", wx, wy, wcs, key, Memc[cmd],
+ SZ_LINE) != EOF) {
+ switch (key) {
+ case '?':
+ call printf (
+ "Hit [spbar=next,-=prev,l=fit,i=resid,s=stats,?=help,q=quit]:")
+ case 'q':
+ call printf ("\n")
+ break
+ case ' ':
+ if (reg < rg_lstati (ls, NREGIONS)) {
+ reg = reg + 1
+ lplot = YES
+ }
+ case '-':
+ if (reg > 1) {
+ reg = reg - 1
+ lplot = YES
+ }
+ case 'l':
+ if (lplot_type == LS_RIRESID)
+ lplot = YES
+ lplot_type = LS_RIFIT
+ case 'i':
+ if (lplot_type == LS_RIFIT)
+ lplot = YES
+ lplot_type = LS_RIRESID
+ case 's':
+ call rg_lpwrec (ls, reg)
+ }
+ if (lplot == YES) {
+ if (lplot_type == LS_RIFIT)
+ stat = rg_rifplot (gd, imr, im1, ls,
+ Memi[udelete], reg)
+ else if (lplot_type == LS_RIRESID)
+ stat = rg_rirplot (gd, imr, im1, ls,
+ Memi[udelete], reg)
+ call printf (
+ "Hit [spbar=next,-=prev,l=fit,i=resid,s=stats,?=help,q=quit]:")
+ lplot = NO
+ }
+ }
+ newplot = YES
+ } else
+ call printf (
+ "The least squares fit is undefined\n")
+
+ # Plot the photometry
+ case 'p':
+ if (rg_lstati(ls,BSALGORITHM) == LS_PHOTOMETRY ||
+ rg_lstati(ls,BZALGORITHM) == LS_PHOTOMETRY) {
+ plottype = LS_MAGSKYFIT
+ newplot = YES
+ } else
+ call printf ("The input photometry is undefined\n")
+
+ # Replot the current graph.
+ case 'r':
+ newplot = YES
+
+ # Delete or undelete a region.
+ case 'd', 'u':
+ if (key == 'd')
+ reg = rg_ldelete (gd, ls, Memi[udelete], wcs, wx, wy,
+ bscale, bzero, plottype, YES)
+ else
+ reg = rg_ldelete (gd, ls, Memi[udelete], wcs, wx, wy,
+ bscale, bzero, plottype, NO)
+ if (reg > 0)
+ newavg = YES
+
+
+ # Process colon commands.
+ case ':':
+ call rg_lcolon (gd, ls, imr, im1, im2, db, dformat,
+ reglist, rpfd, ipfd, sfd, Memc[cmd], newref,
+ newimage, newfit, newavg)
+
+ # Write the parameters to the parameter file.
+ case 'w':
+ call rg_plpars (ls)
+
+ # Do nothing gracefully.
+ default:
+ }
+
+ if (newplot == YES) {
+ if (rg_lstati(ls,NREGIONS) <= 0) {
+ call gclear (gd)
+ call gflush (gd)
+ bscale = 1.0; bzero = 0.0
+ bserr = INDEFR; bzerr = INDEFR
+ call printf ("The regions/photometry list is empty\n")
+ } else if (newref == YES || newimage == YES) {
+ call printf ("Bscale and bzero must be recomputed\n")
+ } else if (rg_lplot (gd, imr, im1, ls, Memi[udelete], 1,
+ bscale, bzero, plottype) == OK) {
+ if (newfit == YES || newavg == YES)
+ call printf ("Bscale and bzero should be recomputed\n")
+ else
+ call rg_lpwrec (ls, 0)
+ newplot = NO
+ } else
+ call printf ("Unable to plot image data for region 1\n")
+ }
+
+ }
+
+ call sfree (sp)
+end
+
+define QUERY "Hit [return=continue, n=next image, q=quit, w=quit and update parameters]: "
+
+# RG_LGQVERIFY -- Print a message on the status line asking the user if they
+# really want to quit, returning YES if they really want to quit, NO otherwise.
+
+int procedure rg_lgqverify (task, db, dformat, rg, ch)
+
+char task[ARB] #I the calling task name
+pointer db #I pointer to the shifts database file
+int dformat #I is the shifts file in database format
+pointer rg #I pointer to the task structure
+int ch #I the input keystroke command
+
+int wcs, stat
+pointer sp, cmd
+real wx, wy
+bool streq()
+int clgcur()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Print the status line query in reverse video and get the keystroke.
+ call printf (QUERY)
+ #call flush (STDOUT)
+ if (clgcur ("gcommands", wx, wy, wcs, ch, Memc[cmd], SZ_LINE) == EOF)
+ ;
+
+ # Process the command.
+ if (ch == 'q') {
+ call rg_lwrec (db, dformat, rg)
+ stat = YES
+ } else if (ch == 'w') {
+ call rg_lwrec (db, dformat, rg)
+ if (streq ("linmatch", task))
+ call rg_plpars (rg)
+ stat = YES
+ } else if (ch == 'n') {
+ call rg_lwrec (db, dformat, rg)
+ stat = YES
+ } else {
+ stat = NO
+ }
+
+ call sfree (sp)
+ return (stat)
+end
+
+
+# RG_LGTVERIFY -- Verify whether or not the user truly wishes to quit the
+# task.
+
+int procedure rg_lgtverify (ch)
+
+int ch #I the input keystroke command
+
+begin
+ if (ch == 'q') {
+ return (YES)
+ } else if (ch == 'w') {
+ return (YES)
+ } else if (ch == 'n') {
+ return (NO)
+ } else {
+ return (NO)
+ }
+end
diff --git a/pkg/images/immatch/src/linmatch/rglpars.x b/pkg/images/immatch/src/linmatch/rglpars.x
new file mode 100644
index 00000000..d5f66320
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rglpars.x
@@ -0,0 +1,104 @@
+include <lexnum.h>
+include "linmatch.h"
+
+
+# RG_GLPARS -- Fetch the algorithm parameters required by the intensity scaling
+# task.
+
+procedure rg_glpars (ls)
+
+pointer ls #I pointer to iscale structure
+
+int ip, nchars
+pointer sp, str1, str2
+int clgeti(), nscan(), lexnum()
+real clgetr()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (str1, SZ_LINE, TY_CHAR)
+ call salloc (str2, SZ_LINE, TY_CHAR)
+
+ # Initialize the linscale structure.
+ call rg_linit (ls, clgeti ("maxnregions"))
+
+ # Get the x and y shifts.
+ call rg_lsetr (ls, XSHIFT, clgetr("xshift"))
+ call rg_lsetr (ls, YSHIFT, clgetr("yshift"))
+
+ # Get the scaling algorithm parameters.
+ call clgstr ("scaling", Memc[str1], SZ_LINE)
+ call sscan (Memc[str1])
+ call gargwrd (Memc[str1], SZ_LINE)
+ call gargwrd (Memc[str2], SZ_LINE)
+ call rg_lsets (ls, BSSTRING, Memc[str1])
+ ip = 1
+ if (nscan() == 2)
+ call rg_lsets (ls, BZSTRING, Memc[str2])
+ else if (lexnum(Memc[str1], ip, nchars) == LEX_NONNUM)
+ call rg_lsets (ls, BZSTRING, Memc[str1])
+ else
+ call rg_lsets (ls, BZSTRING, "0.0")
+
+ call rg_lseti (ls, DNX, clgeti ("dnx"))
+ call rg_lseti (ls, DNY, clgeti ("dny"))
+ call rg_lseti (ls, MAXITER, clgeti ("maxiter"))
+ call rg_lsetr (ls, DATAMIN, clgetr ("datamin"))
+ call rg_lsetr (ls, DATAMAX, clgetr ("datamax"))
+ call rg_lseti (ls, NREJECT, clgeti ("nreject"))
+ call rg_lsetr (ls, LOREJECT, clgetr ("loreject"))
+ call rg_lsetr (ls, HIREJECT, clgetr ("hireject"))
+
+ call clgstr ("gain", Memc[str1], SZ_LINE)
+ call rg_lsets (ls, CCDGAIN, Memc[str1])
+ call clgstr ("readnoise", Memc[str1], SZ_LINE)
+ call rg_lsets (ls, CCDREAD, Memc[str1])
+
+ call sfree (sp)
+end
+
+
+# RG_PLPARS -- Save the intensity scaling parameters in the .par file.
+
+procedure rg_plpars (ls)
+
+pointer ls # pointer to the linscale structure
+
+pointer sp, str1, str2, str
+int rg_lstati()
+real rg_lstatr()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (str1, SZ_LINE, TY_CHAR)
+ call salloc (str2, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Set the x and y shifts parameters.
+ call clputr ("xshift", rg_lstatr (ls, XSHIFT))
+ call clputr ("yshift", rg_lstatr (ls, YSHIFT))
+
+ # Scaling algorithm parameters.
+ call rg_lstats (ls, BSSTRING, Memc[str1], SZ_LINE)
+ call rg_lstats (ls, BZSTRING, Memc[str2], SZ_LINE)
+ call sprintf (Memc[str], SZ_FNAME, "%s %s")
+ call pargstr (Memc[str1])
+ call pargstr (Memc[str2])
+ call clpstr ("scaling", Memc[str])
+ call clputi ("dnx", rg_lstati (ls, DNX))
+ call clputi ("dny", rg_lstati (ls, DNY))
+ call clputi ("maxiter", rg_lstati (ls, MAXITER))
+ call clputr ("datamin", rg_lstatr (ls, DATAMIN))
+ call clputr ("datamax", rg_lstatr (ls, DATAMAX))
+ call clputi ("nreject", rg_lstati (ls, NREJECT))
+ call clputr ("loreject", rg_lstatr (ls, LOREJECT))
+ call clputr ("hireject", rg_lstatr (ls, HIREJECT))
+ call rg_lstats (ls, CCDGAIN, Memc[str], SZ_FNAME)
+ call clpstr ("gain", Memc[str])
+ call rg_lstats (ls, CCDREAD, Memc[str], SZ_FNAME)
+ call clpstr ("readnoise", Memc[str])
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/linmatch/rglplot.x b/pkg/images/immatch/src/linmatch/rglplot.x
new file mode 100644
index 00000000..e46f3bcd
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rglplot.x
@@ -0,0 +1,1592 @@
+include <mach.h>
+include <gset.h>
+include "linmatch.h"
+
+define MINFRACTION 0.01
+define FRACTION 0.05
+
+# XP_LPLOT -- Plot the data.
+
+int procedure rg_lplot (gd, imr, im1, ls, udelete, region, bscale, bzero,
+ plot_type)
+
+pointer gd #I pointer to the graphics stream
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deletions array
+int region #I the current region if applicable
+real bscale #I the computed bscale value
+real bzero #I the computed bzero value
+int plot_type #I the current plot type
+
+int stat
+int rg_mmhplot(), rg_mmfplot(), rg_mmrplot(), rg_rifplot(), rg_rirplot()
+int rg_bzfplot(), rg_bzrplot(), rg_msfplot(), rg_msrplot()
+
+begin
+ stat = OK
+
+ switch (plot_type) {
+ case LS_MMHIST:
+ stat = rg_mmhplot (gd, imr, im1, ls, udelete, region)
+ case LS_MMFIT:
+ stat = rg_mmfplot (gd, ls, udelete, bscale, bzero)
+ case LS_MMRESID:
+ stat = rg_mmrplot (gd, ls, udelete, bscale, bzero)
+ case LS_RIFIT:
+ stat = rg_rifplot (gd, imr, im1, ls, udelete, region)
+ case LS_RIRESID:
+ stat = rg_rirplot (gd, imr, imr, ls, udelete, region)
+ case LS_BSZFIT:
+ stat = rg_bzfplot (gd, ls, udelete, bscale, bzero)
+ case LS_BSZRESID:
+ stat = rg_bzrplot (gd, ls, udelete, bscale, bzero)
+ case LS_MAGSKYFIT:
+ stat = rg_msfplot (gd, ls, udelete, bscale, bzero)
+ case LS_MAGSKYRESID:
+ stat = rg_msrplot (gd, ls, udelete, bscale, bzero)
+ default:
+ stat = ERR
+ }
+
+ return (stat)
+end
+
+
+# RG_MMHPLOT -- Plot the histogram of the data used to compute the mean, median,# and mode.
+
+int procedure rg_mmhplot (gd, imr, im1, ls, udelete, region)
+
+pointer gd #I pointer to the graphics stream
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deleteions array
+int region #I the current region if applicable
+
+int nbinsr, nbins1
+pointer rbuf, ibuf, sp, hgmi, hgmr, image, title, str
+real rsigma, hminr, hmaxr, dhr, isigma, hmin1, hmax1, dh1, ymin, ymax
+int rg_lstati(), rg_limget()
+pointer rg_lstatp()
+
+begin
+ # Get the data.
+ if (imr == NULL || im1 == NULL) {
+ return (ERR)
+ } else if (region == rg_lstati (ls,CNREGION) &&
+ rg_lstatp (ls,RBUF) != NULL && rg_lstatp(ls, IBUF) != NULL) {
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+ } else if (rg_limget (ls, imr, im1, region) == OK) {
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+ } else {
+ return (ERR)
+ }
+
+ # Get the reference image binning parameters.
+ rsigma = sqrt (real(Memi[rg_lstatp(ls,RNPTS)+region-1])) *
+ Memr[rg_lstatp(ls,RSIGMA)+region-1]
+ hminr = Memr[rg_lstatp(ls,RMEDIAN)+region-1] - LMODE_HWIDTH * rsigma
+ hmaxr = Memr[rg_lstatp(ls,RMEDIAN)+region-1] + LMODE_HWIDTH * rsigma
+ dhr = LMODE_ZBIN * rsigma
+ if (dhr <= 0.0)
+ return (ERR)
+ nbinsr = (hmaxr - hminr) / dhr + 1
+ if (nbinsr <= 0)
+ return (ERR)
+
+ # Get the input image binning parameters.
+ isigma = sqrt (real(Memi[rg_lstatp(ls,INPTS)+region-1])) *
+ Memr[rg_lstatp(ls,ISIGMA)+region-1]
+ hmin1 = Memr[rg_lstatp(ls,IMEDIAN)+region-1] - LMODE_HWIDTH * isigma
+ hmax1 = Memr[rg_lstatp(ls,IMEDIAN)+region-1] + LMODE_HWIDTH * isigma
+ dh1 = LMODE_ZBIN * isigma
+ if (dh1 <= 0.0)
+ return (ERR)
+ nbins1 = (hmax1 - hmin1) / dh1 + 1
+ if (nbins1 <= 0.0)
+ return (ERR)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (hgmi, max (nbinsr, nbins1), TY_INT)
+ call salloc (hgmr, max (nbinsr, nbins1), TY_REAL)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ call gclear (gd)
+
+ # Create the reference histogram.
+ call aclri (Memi[hgmi], nbinsr)
+ call ahgmr (Memr[rbuf], Memi[rg_lstatp(ls,RNPTS)+region-1],
+ Memi[hgmi], nbinsr, hminr, hmaxr)
+ call achtir (Memi[hgmi], Memr[hgmr], nbinsr)
+ call alimr (Memr[hgmr], nbinsr, ymin, ymax)
+
+ # Compute the limits for the reference histogram.
+ call gseti (gd, G_WCS, 1)
+ call gsview (gd, 0.1, 0.9, 0.6, 0.9)
+ call gswind (gd, hminr, hmaxr, ymin, ymax)
+ call rg_pfill (gd, hminr, hmaxr, ymin, ymax, GF_SOLID, 0)
+ call rg_lstats (ls, REFIMAGE, Memc[image], SZ_FNAME)
+ call sprintf (Memc[str], SZ_LINE,
+ "Mean = %g Median = %g Mode = %g Sigma = %g")
+ call pargr (Memr[rg_lstatp(ls,RMEAN)+region-1])
+ call pargr (Memr[rg_lstatp(ls,RMEDIAN)+region-1])
+ call pargr (Memr[rg_lstatp(ls,RMODE)+region-1])
+ call pargr (rsigma)
+
+ # Create the title for the reference histogram.
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Ref Image: %s Region: %d%s\nNbins = %d Hmin = %g Hmax = %g Dh = %g\n%s\n")
+ call pargstr (Memc[image])
+ call pargi (region)
+ if (udelete[region] == YES)
+ call pargstr (" [deleted]")
+ else if (Memi[rg_lstatp(ls,RDELETE)+region-1] != LS_NO)
+ call pargstr (" [rejected]")
+ else
+ call pargstr ("")
+ call pargi (nbinsr)
+ call pargr (hminr)
+ call pargr (hmaxr)
+ call pargr (dhr)
+ call pargstr (Memc[str])
+ call gseti (gd, G_YNMINOR, 0)
+ call glabax (gd, Memc[title], "", "")
+
+ # Plot the reference histogram.
+ call rg_lhbox (gd, Memr[hgmr], nbinsr, hminr - dhr / 2.0,
+ hmaxr + dhr / 2.0)
+
+ # Create the input histogram.
+ call aclri (Memi[hgmi], nbins1)
+ call ahgmr (Memr[ibuf], Memi[rg_lstatp(ls,INPTS)+region-1],
+ Memi[hgmi], nbins1, hmin1, hmax1)
+ call achtir (Memi[hgmi], Memr[hgmr], nbins1)
+ call alimr (Memr[hgmr], nbins1, ymin, ymax)
+
+ # Compute the limits for the input histogram.
+ call gseti (gd, G_WCS, 2)
+ call gsview (gd, 0.1, 0.9, 0.1, 0.4)
+ call gswind (gd, hmin1, hmax1, ymin, ymax)
+ call rg_pfill (gd, hmin1, hmax1, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for the input histogram.
+ call rg_lstats (ls, IMAGE, Memc[image], SZ_FNAME)
+ call sprintf (Memc[str], SZ_LINE,
+ "Mean = %g Median = %g Mode = %g Sigma = %g")
+ call pargr (Memr[rg_lstatp(ls,IMEAN)+region-1])
+ call pargr (Memr[rg_lstatp(ls,IMEDIAN)+region-1])
+ call pargr (Memr[rg_lstatp(ls,IMODE)+region-1])
+ call pargr (isigma)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Input Image: %s Region: %d%s\nNbins = %d Hmin = %g Hmax = %g Dh = %g\n%s\n")
+ call pargstr (Memc[image])
+ call pargi (region)
+ if (udelete[region] == YES)
+ call pargstr (" [deleted]")
+ else if (Memi[rg_lstatp(ls,RDELETE)+region-1] != NO)
+ call pargstr (" [rejected]")
+ else
+ call pargstr ("")
+ call pargi (nbins1)
+ call pargr (hmin1)
+ call pargr (hmax1)
+ call pargr (dh1)
+ call pargstr (Memc[str])
+ call gseti (gd, G_YNMINOR, 0)
+ call glabax (gd, Memc[title], "", "")
+
+ # Plot the input histogram.
+ call rg_lhbox (gd, Memr[hgmr], nbins1, hmin1 - dh1 / 2.0,
+ hmax1 + dh1 / 2.0)
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_MMFPLOT -- Plot the fit computed from the mean, median, or mode.
+
+int procedure rg_mmfplot (gd, ls, udelete, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deletions array
+real bscale #I the fitted bscale value
+real bzero #I the fitted bzero value
+
+bool start, finish
+int nregions, mtype
+pointer sp, title, str, imager, image1
+real xmin, xmax, ymin, ymax, diff, dxmin, dxmax, dymin, dymax, x, y
+int rg_lstati()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (ERR)
+
+ # Determine the type of data to plot.
+ mtype = 0
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ if (mtype <= 0)
+ return (ERR)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_LINE, TY_CHAR)
+ call salloc (image1, SZ_LINE, TY_CHAR)
+
+ # Clear the plot space.
+ call gclear (gd)
+
+ # Compute the limits of the plot.
+ switch (mtype) {
+ case LS_MEAN:
+ call rg_galimr (Memr[rg_lstatp(ls,IMEAN)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, xmin, xmax)
+ call rg_galimr (Memr[rg_lstatp(ls,RMEAN)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, ymin, ymax)
+ case LS_MEDIAN:
+ call rg_galimr (Memr[rg_lstatp(ls,IMEDIAN)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, xmin, xmax)
+ call rg_galimr (Memr[rg_lstatp(ls,RMEDIAN)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, ymin, ymax)
+ case LS_MODE:
+ call rg_galimr (Memr[rg_lstatp(ls,IMODE)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, xmin, xmax)
+ call rg_galimr (Memr[rg_lstatp(ls,RMODE)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, ymin, ymax)
+ }
+ dxmin = xmin
+ dxmax = xmax
+ dymin = ymin
+ dymax = ymax
+
+ diff = xmax - xmin
+ if (diff <= 0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (xmax + xmin) / 2.0)
+ xmin = xmin - diff * FRACTION
+ xmax = xmax + diff * FRACTION
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (ymax + ymin) / 2.0)
+ ymin = ymin - diff * FRACTION
+ ymax = ymax + diff * FRACTION
+ call gswind (gd, xmin, xmax, ymin, ymax)
+
+ # Construct the titles and axis labels.
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ call rg_lstats (ls, IMAGE, Memc[image1], SZ_FNAME)
+ call sprintf (Memc[str], SZ_LINE,
+ "Nregions = %d Ref Image = %g * Input Image + %g")
+ call pargi (nregions)
+ call pargr (bscale)
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Counts for %s versus Counts for %s\n%s\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Image Counts",
+ "Ref Image Counts")
+
+ # Plot the data.
+ switch (mtype) {
+ case LS_MEAN:
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMEAN)],
+ Memr[rg_lstatp(ls,RMEAN)], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions, GM_BOX, GM_CROSS)
+ case LS_MEDIAN:
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMEDIAN)],
+ Memr[rg_lstatp(ls,RMEDIAN)], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions, GM_BOX, GM_CROSS)
+ case LS_MODE:
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMODE)],
+ Memr[rg_lstatp(ls,RMODE)], Memi[rg_lstatp(ls,RDELETE)],
+ udelete, nregions, GM_BOX, GM_CROSS)
+ }
+
+ # Plot the fit.
+ start = false
+ finish = false
+ if (! IS_INDEFR(bscale) && ! IS_INDEFR(bzero)) {
+ y = bscale * dxmin + bzero
+ if (y >= ymin && y <= ymax) {
+ call gamove (gd, dxmin, y)
+ start = true
+ }
+ y = bscale * dxmax + bzero
+ if (y >= ymin && y <= ymax) {
+ if (start) {
+ call gadraw (gd, dxmax, y)
+ finish = true
+ } else {
+ call gamove (gd, dxmax, y)
+ start = true
+ }
+ }
+ x = (dymin - bzero) / bscale
+ if (x >= xmin && x <= xmax) {
+ if (! start) {
+ call gamove (gd, x, dymin)
+ start = true
+ } else if (! finish) {
+ call gadraw (gd, x, dymin)
+ finish = true
+ }
+ }
+ x = (dymax - bzero) / bscale
+ if (x >= xmin && x <= xmax) {
+ if (! start) {
+ call gamove (gd, x, dymax)
+ start = true
+ } else if (! finish) {
+ call gadraw (gd, x, dymax)
+ finish = true
+ }
+ }
+ }
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_MMRPLOT -- Plot the residuals from the fit computed from the mean,
+# median, or mode.
+
+int procedure rg_mmrplot (gd, ls, udelete, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deletions array
+real bscale #I the fitted bscale value
+real bzero #I the fitted bzero value
+
+int nregions, mtype
+pointer sp, resid, title, imager, image1, str
+real xmin, xmax, ymin, ymax, diff
+int rg_lstati()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (ERR)
+
+ # Determine the type of data to plot.
+ mtype = 0
+ switch (rg_lstati(ls, BSALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ switch (rg_lstati(ls, BZALGORITHM)) {
+ case LS_MEAN:
+ mtype = LS_MEAN
+ case LS_MEDIAN:
+ mtype = LS_MEDIAN
+ case LS_MODE:
+ mtype = LS_MODE
+ default:
+ }
+ if (mtype <= 0)
+ return (ERR)
+
+ # Allocate working space.
+ call smark (sp)
+
+ call gclear (gd)
+
+ # Compute the data.
+ call salloc (resid, nregions, TY_REAL)
+ switch (mtype) {
+ case LS_MEAN:
+ call altmr (Memr[rg_lstatp(ls,IMEAN)], Memr[resid], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMEAN)], Memr[resid], Memr[resid],
+ nregions)
+ call rg_galimr (Memr[rg_lstatp(ls,IMEAN)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, xmin, xmax)
+ call rg_galimr (Memr[resid], Memi[rg_lstatp(ls,RDELETE)], nregions,
+ ymin, ymax)
+ case LS_MEDIAN:
+ call altmr (Memr[rg_lstatp(ls,IMEDIAN)], Memr[resid], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMEDIAN)], Memr[resid], Memr[resid],
+ nregions)
+ call rg_galimr (Memr[rg_lstatp(ls,IMEDIAN)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, xmin, xmax)
+ call rg_galimr (Memr[resid], Memi[rg_lstatp(ls,RDELETE)], nregions,
+ ymin, ymax)
+ case LS_MODE:
+ call altmr (Memr[rg_lstatp(ls,IMODE)], Memr[resid], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RMODE)], Memr[resid], Memr[resid],
+ nregions)
+ call rg_galimr (Memr[rg_lstatp(ls,IMODE)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, xmin, xmax)
+ call rg_galimr (Memr[resid], Memi[rg_lstatp(ls,RDELETE)], nregions,
+ ymin, ymax)
+ }
+
+ # Compute the data limits.
+ diff = xmax - xmin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (xmax + xmin) / 2.0)
+ xmin = xmin - diff * FRACTION
+ xmax = xmax + diff * FRACTION
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (ymax + ymin) / 2.0)
+ ymin = ymin - diff * FRACTION
+ ymax = ymax + diff * FRACTION
+ call gswind (gd, xmin, xmax, ymin, ymax)
+
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ call rg_lstats (ls, IMAGE, Memc[image1], SZ_FNAME)
+ call sprintf (Memc[str], SZ_LINE,
+ "Nregions = %d Ref Image = %g * Input Image + %g")
+ call pargi (nregions)
+ call pargr (bscale)
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Residuals for %s versus Counts for %s\n%s\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Image Counts",
+ "Residual Counts")
+
+ # Plot the data.
+ switch (mtype) {
+ case LS_MEAN:
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMEAN)], Memr[resid],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions,
+ GM_BOX, GM_CROSS)
+ case LS_MEDIAN:
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMEDIAN)], Memr[resid],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions,
+ GM_BOX, GM_CROSS)
+ case LS_MODE:
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMODE)], Memr[resid],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions,
+ GM_BOX, GM_CROSS)
+ }
+
+ # Plot the residuals 0 line.
+ call gamove (gd, xmin, 0.0)
+ call gadraw (gd, xmax, 0.0)
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_RIFPLOT -- Plot the pixel to pixel fit for a region.
+
+int procedure rg_rifplot (gd, imr, im1, ls, udelete, region)
+
+pointer gd #I pointer to the graphics stream
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I pointer to the user deletions array
+int region #I the current region
+
+bool start, finish
+int npts
+pointer rbuf, ibuf, sp, title, str, imager, image1, resid
+real xmin, xmax, ymin, ymax, diff, bscale, bzero, datamin, datamax
+real loreject, hireject, chi, dxmin, dxmax, dymin, dymax, x, y
+int rg_lstati(), rg_limget()
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ # Get the data.
+ if (imr == NULL || im1 == NULL) {
+ return (ERR)
+ } else if (region == rg_lstati (ls,CNREGION) &&
+ rg_lstatp (ls,RBUF) != NULL && rg_lstatp(ls, IBUF) != NULL) {
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+ } else if (rg_limget (ls, imr, im1, region) == OK) {
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+ } else {
+ return (ERR)
+ }
+
+ # Initialize.
+ call gclear (gd)
+
+ # Get some constants
+ npts = Memi[rg_lstatp(ls,RNPTS)+region-1]
+ bscale = Memr[rg_lstatp(ls,RBSCALE)+region-1]
+ bzero = Memr[rg_lstatp(ls,RBZERO)+region-1]
+ chi = Memr[rg_lstatp(ls,RCHI)+region-1]
+ if (IS_INDEFR(rg_lstatr(ls,DATAMIN)))
+ datamin = -MAX_REAL
+ else
+ datamin = rg_lstatr (ls,DATAMIN)
+ if (IS_INDEFR(rg_lstatr(ls,DATAMAX)))
+ datamax = MAX_REAL
+ else
+ datamax = rg_lstatr (ls,DATAMAX)
+ if (rg_lstati(ls,NREJECT) <= 0 || IS_INDEFR(rg_lstatr(ls,LOREJECT)) ||
+ IS_INDEFR(chi))
+ loreject = -MAX_REAL
+ else
+ loreject = -rg_lstatr (ls,LOREJECT) * chi
+ if (rg_lstati(ls,NREJECT) <= 0 || IS_INDEFR(rg_lstatr(ls,HIREJECT)) ||
+ IS_INDEFR(chi))
+ hireject = MAX_REAL
+ else
+ hireject = rg_lstatr (ls,HIREJECT) * chi
+
+ # Compute the plot limits.
+ call alimr (Memr[ibuf], npts, xmin, xmax)
+ call alimr (Memr[rbuf], npts, ymin, ymax)
+ dxmin = xmin
+ dxmax = xmax
+ dymin = ymin
+ dymax = ymax
+
+ diff = xmax - xmin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (xmax + xmin) / 2.0)
+ xmin = xmin - diff * FRACTION
+ xmax = xmax + diff * FRACTION
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (ymax + ymin) / 2.0)
+ ymin = ymin - diff * FRACTION
+ ymax = ymax + diff * FRACTION
+ call gswind (gd, xmin, xmax, ymin, ymax)
+
+ # Allocate working space.
+ call smark (sp)
+
+ # Create the plot title.
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ call rg_lstats (ls, IMAGE, Memc[image1], SZ_FNAME)
+ call sprintf (Memc[str], SZ_LINE,
+ "Region %d%s: Ref Image = %g * Input Image + %g")
+ call pargi (region)
+ if (udelete[region] == YES)
+ call pargstr (" [deleted]")
+ else if (Memi[rg_lstatp(ls,RDELETE)+region-1] != LS_NO)
+ call pargstr (" [rejected]")
+ else
+ call pargstr ("")
+ call pargr (bscale)
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Counts for Image %s versus Counts for Image %s\n%s\n\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Image Counts",
+ "Ref image Counts")
+
+ # Compute the residuals.
+ call salloc (resid, npts, TY_REAL)
+ if (IS_INDEFR(bscale) || IS_INDEFR(bzero))
+ call amovkr (0.0, Memr[resid], npts)
+ else {
+ call altmr (Memr[ibuf], Memr[resid], npts, bscale, bzero)
+ call asubr (Memr[rbuf], Memr[resid], Memr[resid], npts)
+ }
+
+ # Plot the data.
+ call rg_riplot (gd, Memr[ibuf], Memr[rbuf], Memr[resid], npts,
+ datamin, datamax, loreject, hireject, GM_BOX, GM_CROSS)
+
+ # Plot the fit if bscale and bzero are defined.
+ start = false
+ finish = false
+ if (! IS_INDEFR(bscale) && ! IS_INDEFR(bzero)) {
+ y = bscale * dxmin + bzero
+ if (y >= ymin && y <= ymax) {
+ call gamove (gd, dxmin, y)
+ start = true
+ }
+ y = bscale * dxmax + bzero
+ if (y >= ymin && y <= ymax) {
+ if (start) {
+ call gadraw (gd, dxmax, y)
+ finish = true
+ } else {
+ call gamove (gd, dxmax, y)
+ start = true
+ }
+ }
+ x = (dymin - bzero) / bscale
+ if (x >= xmin && x <= xmax) {
+ if (! start) {
+ call gamove (gd, x, dymin)
+ start = true
+ } else if (! finish) {
+ call gadraw (gd, x, dymin)
+ finish = true
+ }
+ }
+ x = (dymax - bzero) / bscale
+ if (x >= xmin && x <= xmax) {
+ if (! start) {
+ call gamove (gd, x, dymax)
+ start = true
+ } else if (! finish) {
+ call gadraw (gd, x, dymax)
+ finish = true
+ }
+ }
+ }
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_RIRPLOT -- Plot the pixel to pixel fit residuals for a region.
+
+int procedure rg_rirplot (gd, imr, im1, ls, udelete, region)
+
+pointer gd #I pointer to the graphics stream
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I pointer to the user deletions array
+int region #I the current region
+
+int npts
+pointer rbuf, ibuf, sp, title, str, imager, image1, resid
+real xmin, xmax, ymin, ymax, diff, bscale, bzero, datamin, datamax
+real loreject, hireject, chi
+int rg_lstati(), rg_limget()
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ # Get the data.
+ if (imr == NULL || im1 == NULL) {
+ return (ERR)
+ } else if (region == rg_lstati (ls,CNREGION) &&
+ rg_lstatp (ls,RBUF) != NULL && rg_lstatp(ls, IBUF) != NULL) {
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+ } else if (rg_limget (ls, imr, im1, region) == OK) {
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+ } else {
+ return (ERR)
+ }
+
+ # Initialize.
+ call gclear (gd)
+
+ # Get some constants
+ npts = Memi[rg_lstatp(ls,RNPTS)+region-1]
+ bscale = Memr[rg_lstatp(ls,RBSCALE)+region-1]
+ bzero = Memr[rg_lstatp(ls,RBZERO)+region-1]
+ chi = Memr[rg_lstatp(ls,RCHI)+region-1]
+ if (IS_INDEFR(rg_lstatr(ls,DATAMIN)))
+ datamin = -MAX_REAL
+ else
+ datamin = rg_lstatr (ls,DATAMIN)
+ if (IS_INDEFR(rg_lstatr(ls,DATAMAX)))
+ datamax = MAX_REAL
+ else
+ datamax = rg_lstatr (ls,DATAMAX)
+ if (rg_lstati(ls,NREJECT) <= 0 || IS_INDEFR(rg_lstatr(ls,LOREJECT)) ||
+ IS_INDEFR(chi))
+ loreject = -MAX_REAL
+ else
+ loreject = -rg_lstatr (ls,LOREJECT) * chi
+ if (rg_lstati(ls,NREJECT) <= 0 || IS_INDEFR(rg_lstatr(ls,HIREJECT)) ||
+ IS_INDEFR(chi))
+ hireject = MAX_REAL
+ else
+ hireject = rg_lstatr (ls,HIREJECT) * chi
+
+ # Allocate working space.
+ call smark (sp)
+
+ # Compute the residuals.
+ call salloc (resid, npts, TY_REAL)
+ if (IS_INDEFR(bscale) || IS_INDEFR(bzero))
+ call amovkr (INDEFR, Memr[resid], npts)
+ else {
+ call altmr (Memr[ibuf], Memr[resid], npts, bscale, bzero)
+ call asubr (Memr[rbuf], Memr[resid], Memr[resid], npts)
+ }
+
+ # Compute the plot limits.
+ call alimr (Memr[ibuf], npts, xmin, xmax)
+ call alimr (Memr[resid], npts, ymin, ymax)
+ diff = xmax - xmin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (xmin + xmax) / 2.0)
+ xmin = xmin - diff * FRACTION
+ xmax = xmax + diff * FRACTION
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (ymin + ymax) / 2.0)
+ ymin = ymin - diff * FRACTION
+ ymax = ymax + diff * FRACTION
+ call gswind (gd, xmin, xmax, ymin, ymax)
+
+ # Create the plot title.
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+
+ # Create the plot title.
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ call rg_lstats (ls, IMAGE, Memc[image1], SZ_FNAME)
+ call sprintf (Memc[str], SZ_LINE,
+ "Region %d%s: Ref Image = %g * Input Image + %g")
+ call pargi (region)
+ if (udelete[region] == YES)
+ call pargstr (" [deleted]")
+ else if (Memi[rg_lstatp(ls,RDELETE)+region-1] != LS_NO)
+ call pargstr (" [rejected]")
+ else
+ call pargstr ("")
+ call pargr (bscale)
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Residuals for Image %s versus Counts for Image %s\n%s\n\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Image Counts",
+ "Ref image Counts")
+
+ # Plot the data.
+ call rg_rriplot (gd, Memr[ibuf], Memr[rbuf], Memr[resid], npts,
+ datamin, datamax, loreject, hireject, GM_BOX, GM_CROSS)
+
+ # Plot the 0 line if bscale and bzero are defined.
+ if ( ! IS_INDEFR(bscale) && ! IS_INDEFR(bzero)) {
+ call gamove (gd, xmin, 0.0)
+ call gadraw (gd, xmax, 0.0)
+ }
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_BZFPLOT -- Plot the bscale and bzero values computed from the
+# fit algorithm.
+
+int procedure rg_bzfplot (gd, ls, udelete, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deletions array
+real bscale #I the fitted bscale value
+real bzero #I the fitted bzero value
+
+int i, nregions
+pointer sp, xreg, title, str, imager, image1
+real xmin, xmax, ymin, ymax, diff
+int rg_lstati()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (ERR)
+
+ # Allocate working space.
+ call smark (sp)
+
+ # Set up space and info the plot title.
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ if (rg_lstati(ls,BSALGORITHM) == LS_PHOTOMETRY ||
+ rg_lstati(ls,BZALGORITHM) == LS_PHOTOMETRY)
+ call rg_lstats (ls, PHOTFILE, Memc[image1], SZ_FNAME)
+ else
+ call rg_lstats (ls, IMAGE, Memc[image1], SZ_FNAME)
+
+ # Set the x array.
+ call salloc (xreg, nregions, TY_REAL)
+ do i = 1, nregions
+ Memr[xreg+i-1] = i
+ xmin = 1.0 - FRACTION * (nregions - 1)
+ xmax = nregions + FRACTION * (nregions - 1)
+
+ call gclear (gd)
+
+ # Determine the limits of bscale versus region.
+ call alimr (Memr[rg_lstatp(ls,RBSCALE)], nregions, ymin, ymax)
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (ymax + ymin) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 1)
+ call gsview (gd, 0.15, 0.9, 0.6, 0.9)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bscale versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference: %s Input: %s Bscale: %g")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargr (bscale)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Bscale vs. Region\n%s\n")
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Region", "Bscale")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[xreg], Memr[rg_lstatp(ls,RBSCALE)],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ call gamove (gd, xmin, bscale)
+ call gadraw (gd, xmax, bscale)
+
+ # Determine the limits of bzero versus region.
+ call alimr (Memr[rg_lstatp(ls,RBZERO)], nregions, ymin, ymax)
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * abs (ymin + ymax) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 2)
+ call gsview (gd, 0.15, 0.9, 0.1, 0.4)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bzero versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference: %s Input: %s Bzero: %g")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE, "Bzero vs. Region\n%s\n")
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Region", "Bzero")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[xreg], Memr[rg_lstatp(ls,RBZERO)],
+ Memi[rg_lstatp(ls,RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ call gamove (gd, xmin, bzero)
+ call gadraw (gd, xmax, bzero)
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_BZRPLOT -- Plot the bscale and bzero values computed from the
+# fit algorithm.
+
+int procedure rg_bzrplot (gd, ls, udelete, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deletions array
+real bscale #I the fitted bscale value
+real bzero #I the fitted bzero value
+
+int i, nregions
+pointer sp, xreg, yreg, title, str, imager, image1
+real xmin, xmax, ymin, ymax, diff
+int rg_lstati()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 1)
+ return (ERR)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (xreg, nregions, TY_REAL)
+ call salloc (yreg, nregions, TY_REAL)
+
+ # Set up space and info the plot title.
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ if (rg_lstati(ls,BSALGORITHM) == LS_PHOTOMETRY ||
+ rg_lstati(ls,BZALGORITHM) == LS_PHOTOMETRY)
+ call rg_lstats (ls, PHOTFILE, Memc[image1], SZ_FNAME)
+ else
+ call rg_lstats (ls, IMAGE, Memc[image1], SZ_FNAME)
+
+ # Set the x array.
+ do i = 1, nregions
+ Memr[xreg+i-1] = i
+ xmin = 1.0 - FRACTION * (nregions - 1)
+ xmax = nregions + FRACTION * (nregions - 1)
+
+ call gclear (gd)
+
+ # Determine the limits of the bscale value versus region.
+ call asubkr (Memr[rg_lstatp(ls,RBSCALE)], bscale, Memr[yreg], nregions)
+ call alimr (Memr[yreg], nregions, ymin, ymax)
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (ymax + ymin) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 1)
+ call gsview (gd, 0.15, 0.9, 0.6, 0.9)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bscale versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference: %s Input: %s Bscale: %g")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargr (bscale)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Bscale Residuals vs. Region\n%s\n")
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Region", "Bscale Residuals")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[xreg], Memr[yreg], Memi[rg_lstatp(ls,
+ RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ call gamove (gd, xmin, 0.0)
+ call gadraw (gd, xmax, 0.0)
+
+ # Determine the limits of the bscale value versus region.
+ call asubkr (Memr[rg_lstatp(ls,RBZERO)], bzero, Memr[yreg], nregions)
+ call alimr (Memr[yreg], nregions, ymin, ymax)
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (ymax + ymin) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 2)
+ call gsview (gd, 0.15, 0.9, 0.1, 0.4)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bzero versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference: %s Input: %s Bzero: %g")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Bzero Residuals vs. Region\n%s\n")
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Region", "Bzero Residuals")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[xreg], Memr[yreg], Memi[rg_lstatp(ls,
+ RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ call gamove (gd, xmin, 0.0)
+ call gadraw (gd, xmax, 0.0)
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_MSFPLOT -- Plot the magnitude and sky values of the regions.
+
+int procedure rg_msfplot (gd, ls, udelete, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deletions array
+real bscale #I the fitted bscale value
+real bzero #I the fitted bzero value
+
+bool start, finish
+int nregions
+pointer sp, title, str, imager, image1
+real xmin, xmax, ymin, ymax, diff, dxmin, dxmax, dymin, dymax, x, y
+int rg_lstati()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 0)
+ return (ERR)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ call rg_lstats (ls, PHOTFILE, Memc[image1], SZ_FNAME)
+
+ call gclear (gd)
+
+ # Determine the limits of the bscale value versus region.
+ call alimr (Memr[rg_lstatp(ls,IMAG)], nregions, xmin, xmax)
+ dxmin = xmin
+ dxmax = xmax
+ diff = xmax - xmin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (xmax + xmin) / 2.0)
+ xmin = xmin - FRACTION * diff
+ xmax = xmax + FRACTION * diff
+ call alimr (Memr[rg_lstatp(ls,RMAG)], nregions, ymin, ymax)
+ dymin = ymin
+ dymax = ymax
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (ymax + ymin) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 1)
+ call gsview (gd, 0.15, 0.9, 0.6, 0.9)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bscale versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference magnitudes = Input magnitudes + %0.3f")
+ call pargr (-2.5 * log10 (bscale))
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Magnitudes for %s vs. Magnitudes for %s\n%s\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Magnitudes",
+ "Ref Magnitudes")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMAG)], Memr[rg_lstatp(ls,RMAG)],
+ Memi[rg_lstatp(ls, RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ if (bscale > 0.0) {
+ call gamove (gd, dxmin, dxmin - 2.5 * log10(bscale))
+ call gadraw (gd, dxmax, dxmax - 2.5 * log10(bscale))
+ }
+
+ # Determine the limits of the bscale value versus region.
+ call alimr (Memr[rg_lstatp(ls,ISKY)], nregions, xmin, xmax)
+ dxmin = xmin
+ dxmax = xmax
+ diff = xmax - xmin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (xmax + xmin) / 2.0)
+ xmin = xmin - FRACTION * diff
+ xmax = xmax + FRACTION * diff
+ call alimr (Memr[rg_lstatp(ls,RSKY)], nregions, ymin, ymax)
+ dymin = ymin
+ dymax = ymax
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = 0.0
+ else
+ diff = max (diff, MINFRACTION * (ymax + ymin) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 2)
+ call gsview (gd, 0.15, 0.9, 0.1, 0.4)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bscale versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference skies = %g * Input skies + %g")
+ call pargr (bscale)
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Sky Values for %s vs. Sky Values for %s\n%s\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Sky Values",
+ "Ref Sky Values")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,ISKY)], Memr[rg_lstatp(ls,RSKY)],
+ Memi[rg_lstatp(ls, RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ start = false
+ finish = false
+ if (! IS_INDEFR(bscale) && ! IS_INDEFR(bzero)) {
+ y = bscale * dxmin + bzero
+ if (y >= ymin && y <= ymax) {
+ call gamove (gd, dxmin, y)
+ start = true
+ }
+ y = bscale * dxmax + bzero
+ if (y >= ymin && y <= ymax) {
+ if (start) {
+ call gadraw (gd, dxmax, y)
+ finish = true
+ } else {
+ call gamove (gd, dxmax, y)
+ start = true
+ }
+ }
+ x = (dymin - bzero) / bscale
+ if (x >= xmin && x <= xmax) {
+ if (! start) {
+ call gamove (gd, x, dymin)
+ start = true
+ } else if (! finish) {
+ call gadraw (gd, x, dymin)
+ finish = true
+ }
+ }
+ x = (dymax - bzero) / bscale
+ if (x >= xmin && x <= xmax) {
+ if (! start) {
+ call gamove (gd, x, dymax)
+ start = true
+ } else if (! finish) {
+ call gadraw (gd, x, dymax)
+ finish = true
+ }
+ }
+ }
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_MSRPLOT -- Plot the magnitude and sky values of the regions.
+
+int procedure rg_msrplot (gd, ls, udelete, bscale, bzero)
+
+pointer gd #I pointer to the graphics stream
+pointer ls #I pointer to the linmatch structure
+int udelete[ARB] #I the user deletions array
+real bscale #I the fitted bscale value
+real bzero #I the fitted bzero value
+
+int nregions
+pointer sp, yreg, title, str, imager, image1
+real xmin, xmax, ymin, ymax, diff, dmin, dmax
+int rg_lstati()
+pointer rg_lstatp()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions <= 0)
+ return (ERR)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (yreg, nregions, TY_REAL)
+ call salloc (title, 2 * SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call rg_lstats (ls, REFIMAGE, Memc[imager], SZ_FNAME)
+ call rg_lstats (ls, PHOTFILE, Memc[image1], SZ_FNAME)
+
+ call gclear (gd)
+
+ # Determine the limits of the bscale value versus region.
+ call alimr (Memr[rg_lstatp(ls,IMAG)], nregions, xmin, xmax)
+ diff = xmax - xmin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (xmax + xmin) / 2.0)
+ dmin = xmin
+ dmax = xmax
+ xmin = xmin - FRACTION * diff
+ xmax = xmax + FRACTION * diff
+ if (bscale > 0) {
+ call aaddkr (Memr[rg_lstatp(ls,IMAG)], -2.5*log10(bscale),
+ Memr[yreg], nregions)
+ call asubr (Memr[rg_lstatp(ls,RMAG)], Memr[yreg], Memr[yreg],
+ nregions)
+ } else
+ call asubr (Memr[rg_lstatp(ls,RMAG)], Memr[rg_lstatp(ls,IMAG)],
+ Memr[yreg], nregions)
+ call alimr (Memr[yreg], nregions, ymin, ymax)
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (ymax + ymin) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 1)
+ call gsview (gd, 0.15, 0.9, 0.6, 0.9)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bscale versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference: %s Input: %s Bscale: %g")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargr (bscale)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Residuals for %s vs. Magnitudes for %s\n%s\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Magnitudes",
+ "Mag Residuals")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,IMAG)], Memr[yreg],
+ Memi[rg_lstatp(ls, RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ if (bscale > 0.0) {
+ call gamove (gd, xmin, 0.0)
+ call gadraw (gd, xmax, 0.0)
+ }
+
+ # Determine the limits of the bscale value versus region.
+ call alimr (Memr[rg_lstatp(ls,ISKY)], nregions, xmin, xmax)
+ diff = xmax - xmin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (xmax + xmin) / 2.0)
+ dmin = xmin
+ dmax = xmax
+ xmin = xmin - FRACTION * diff
+ xmax = xmax + FRACTION * diff
+ call altmr (Memr[rg_lstatp(ls,ISKY)], Memr[yreg], nregions,
+ bscale, bzero)
+ call asubr (Memr[rg_lstatp(ls,RSKY)], Memr[yreg], Memr[yreg],
+ nregions)
+ call alimr (Memr[yreg], nregions, ymin, ymax)
+ diff = ymax - ymin
+ if (diff <= 0.0)
+ diff = MINFRACTION
+ else
+ diff = max (diff, MINFRACTION * (ymax + ymin) / 2.0)
+ ymin = ymin - FRACTION * diff
+ ymax = ymax + FRACTION * diff
+ call gseti (gd, G_WCS, 2)
+ call gsview (gd, 0.15, 0.9, 0.1, 0.4)
+ call gswind (gd, xmin, xmax, ymin, ymax)
+ call rg_pfill (gd, xmin, xmax, ymin, ymax, GF_SOLID, 0)
+
+ # Create the title for bscale versus region.
+ call sprintf (Memc[str], SZ_LINE,
+ "Reference: %s Input: %s Bscale: %g Bzero: %g")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargr (bscale)
+ call pargr (bzero)
+ call sprintf (Memc[title], 2 * SZ_LINE,
+ "Residuals for %s vs. Sky Values for %s\n%s\n")
+ call pargstr (Memc[imager])
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call glabax (gd, Memc[title], "Input Sky Values",
+ "Sky Residuals")
+
+ # Plot the points.
+ call rg_lxyplot (gd, Memr[rg_lstatp(ls,ISKY)], Memr[yreg],
+ Memi[rg_lstatp(ls, RDELETE)], udelete, nregions, GM_BOX, GM_CROSS)
+
+ # Plot the fit.
+ call gamove (gd, xmin, 0.0)
+ call gadraw (gd, xmax, 0.0)
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_LHBOX -- Draw a stepped curve of the histogram data.
+
+procedure rg_lhbox (gp, ydata, npts, x1, x2)
+
+pointer gp #I the graphics descriptor
+real ydata[ARB] #I the y coordinates of the line endpoints
+int npts #I the number of line endpoints
+real x1, x2 #I starting and ending x coordinates
+
+int pixel
+real left, right, top, bottom, x, y, dx
+
+begin
+ call ggwind (gp, left, right, bottom, top)
+ dx = (x2 - x1) / npts
+
+ # Do the first vertical line.
+ call gamove (gp, x1, bottom)
+ call gadraw (gp, x1, ydata[1])
+
+ # Do the first horizontal line.
+ call gadraw (gp, x1 + dx, ydata[1])
+
+ # Draw the remaining horizontal lines.
+ do pixel = 2, npts {
+ x = x1 + dx * (pixel - 1)
+ y = ydata[pixel]
+ call gadraw (gp, x, y)
+ call gadraw (gp, x + dx, y)
+ }
+
+ # Draw the last vertical line.
+ call gadraw (gp, x + dx, bottom)
+end
+
+
+# RG_PFILL -- Fill a rectangular area with a given style and color.
+
+procedure rg_pfill (gd, xmin, xmax, ymin, ymax, fstyle, fcolor)
+
+pointer gd #I pointer to the graphics stream
+real xmin, xmax #I the x coordinate limits
+real ymin, ymax #I the y coordinate limits
+int fstyle #I the fill style
+int fcolor #I the fill color
+
+real x[4], y[4]
+
+begin
+ call gseti (gd, G_FACOLOR, fcolor)
+ x[1] = xmin; y[1] = ymin
+ x[2] = xmax; y[2] = ymin
+ x[3] = xmax; y[3] = ymax
+ x[4] = xmin; y[4] = ymax
+ call gfill (gd, x, y, 4, fstyle)
+end
+
+
+# XP_LXYPLOT -- Plot the x and y points.
+
+procedure rg_lxyplot (gd, x, y, del, udel, npts, gmarker, dmarker)
+
+pointer gd # pointer to the graphics stream
+real x[ARB] # the x coordinates
+real y[ARB] # the y coordinates
+int del[ARB] # the deletions array
+int udel[ARB] # the user deletions array
+int npts # the number of points to be marked
+int gmarker # the good point marker type
+int dmarker # the deleted point marker type
+
+int i
+
+begin
+ # Plot the points.
+ do i = 1, npts {
+ if (udel[i] == YES) {
+ call gmark (gd, x[i], y[i], gmarker, 2.0, 2.0)
+ call gmark (gd, x[i], y[i], dmarker, 2.0, 2.0)
+ } else if (del[i] != LS_NO)
+ call gmark (gd, x[i], y[i], dmarker, 2.0, 2.0)
+ else
+ call gmark (gd, x[i], y[i], gmarker, 2.0, 2.0)
+ }
+end
+
+
+# XP_RIPLOT -- Plot the reference image intensity versus the input image
+# intensity.
+
+procedure rg_riplot (gd, x, y, resid, npts, datamin, datamax, loreject,
+ hireject, gmarker, dmarker)
+
+pointer gd #I pointer to the graphics stream
+real x[ARB] #I the x coordinates
+real y[ARB] #I the y coordinates
+real resid[ARB] #I the residuals array
+int npts #I the number of points to be marked
+real datamin #I the good data minimum
+real datamax #I the good data maximum
+real loreject #I the low side rejection limit
+real hireject #I the high side rejection limit
+int gmarker #I the good point marker type
+int dmarker #I the deleted point marker type
+
+int i
+
+begin
+ do i = 1, npts {
+ if (x[i] < datamin || x[i] > datamax)
+ call gmark (gd, x[i], y[i], dmarker, 2.0, 2.0)
+ else if (y[i] < datamin || y[i] > datamax)
+ call gmark (gd, x[i], y[i], dmarker, 2.0, 2.0)
+ else if (resid[i] < loreject || resid[i] > hireject)
+ call gmark (gd, x[i], y[i], dmarker, 2.0, 2.0)
+ else
+ call gmark (gd, x[i], y[i], gmarker, 2.0, 2.0)
+ }
+end
+
+
+# XP_RRIPLOT -- Plot the reference image intensity versus the input image
+# intensity.
+
+procedure rg_rriplot (gd, x, y, resid, npts, datamin, datamax, loreject,
+ hireject, gmarker, dmarker)
+
+pointer gd #I pointer to the graphics stream
+real x[ARB] #I the x coordinates
+real y[ARB] #I the y coordinates
+real resid[ARB] #I the residuals array
+int npts #I the number of points to be marked
+real datamin #I the good data minimum
+real datamax #I the good data maximum
+real loreject #I the low side rejection limit
+real hireject #I the high side rejection limit
+int gmarker #I the good point marker type
+int dmarker #I the deleted point marker type
+
+int i
+
+begin
+ do i = 1, npts {
+ if (x[i] < datamin || x[i] > datamax)
+ call gmark (gd, x[i], resid[i], dmarker, 2.0, 2.0)
+ else if (y[i] < datamin || y[i] > datamax)
+ call gmark (gd, x[i], resid[i], dmarker, 2.0, 2.0)
+ else if (IS_INDEFR(resid[i]))
+ call gmark (gd, x[i], resid[i], dmarker, 2.0, 2.0)
+ else if (resid[i] < loreject || resid[i] > hireject)
+ call gmark (gd, x[i], resid[i], dmarker, 2.0, 2.0)
+ else
+ call gmark (gd, x[i], resid[i], gmarker, 2.0, 2.0)
+ }
+end
+
+
+# RG_GALIMR -- Compute the good data limits for the plot.
+
+procedure rg_galimr (a, index, npts, amin, amax)
+
+real a[ARB] #I the input array
+int index[ARB] #I the index array
+int npts #I the size of the array
+real amin, amax #O the output min and max values
+
+int i
+real dmin, dmax, gmin, gmax
+
+begin
+ dmin = a[1]; dmax = a[1]
+ gmin = MAX_REAL; gmax = -MAX_REAL
+
+ do i = 1, npts {
+ if (a[i] < dmin)
+ dmin = a[i]
+ else if (a[i] > dmax)
+ dmax = a[i]
+ if (index[i] == LS_NO) {
+ if (a[i] < gmin)
+ gmin = a[i]
+ if (a[i] > gmax)
+ gmax = a[i]
+ }
+ }
+
+ if (gmin == MAX_REAL)
+ amin = dmin
+ else
+ amin = gmin
+ if (gmax == -MAX_REAL)
+ amax = dmax
+ else
+ amax = gmax
+end
diff --git a/pkg/images/immatch/src/linmatch/rglregions.x b/pkg/images/immatch/src/linmatch/rglregions.x
new file mode 100644
index 00000000..16f01b15
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rglregions.x
@@ -0,0 +1,1084 @@
+include <ctype.h>
+include <fset.h>
+include <imhdr.h>
+include "linmatch.h"
+
+# RG_LREGIONS -- Decode the input regions description. If the regions string
+# is NULL then the regions list is empty. The regions are specified in section
+# notation, grid notation, coordinate notation or are read
+# from a file.
+
+int procedure rg_lregions (list, im, ls, rp, reread)
+
+int list #I pointer to the regions file list
+pointer im #I pointer to the reference image
+pointer ls #I pointer to the linscale structure
+int rp #I region pointer
+int reread #I reread the current file
+
+char fname[SZ_FNAME]
+int max_nregions, nregions, fd
+pointer sp, regions
+int rg_lstati(), rg_lgrid(), rg_lgregions(), rg_lsregions()
+int rg_lrsections(), rg_lrcoords(), fntgfnb(), open()
+data fname[1] /EOS/
+errchk fntgfnb(), seek(), open(), close()
+
+begin
+ call smark (sp)
+ call salloc (regions, SZ_LINE, TY_CHAR)
+
+ call rg_lstats (ls, REGIONS, Memc[regions], SZ_LINE)
+ max_nregions = rg_lstati (ls, MAXNREGIONS)
+
+ if (rp < 1 || rp > max_nregions || Memc[regions] == EOS) {
+ nregions = 0
+ } else if (rg_lgrid (im, ls, rp, max_nregions) > 0) {
+ nregions = rg_lstati (ls, NREGIONS)
+ } else if (rg_lgregions (im, ls, rp, max_nregions) > 0) {
+ nregions = rg_lstati (ls, NREGIONS)
+ } else if (rg_lsregions (im, ls, rp, max_nregions) > 0) {
+ nregions = rg_lstati (ls, NREGIONS)
+ } else if (list != NULL) {
+ if (reread == NO) {
+ iferr {
+ if (fntgfnb (list, fname, SZ_FNAME) != EOF) {
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+ nregions= rg_lrsections (fd, im, ls, rp, max_nregions)
+ if (nregions <= 0) {
+ call seek (fd, BOF)
+ nregions= rg_lrcoords (fd, im, ls, rp, max_nregions)
+ }
+ call close (fd)
+ } else
+ nregions = 0
+ } then
+ nregions = 0
+ } else if (fname[1] != EOS) {
+ iferr {
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+ nregions= rg_lrsections (fd, im, ls, rp, max_nregions)
+ if (nregions <= 0) {
+ call seek (fd, BOF)
+ nregions= rg_lrcoords (fd, im, ls, rp, max_nregions)
+ }
+ call close (fd)
+ } then
+ nregions = 0
+ }
+ } else
+ nregions = 0
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_LGRID - Decode the regions from a grid specification.
+
+int procedure rg_lgrid (im, ls, rp, max_nregions)
+
+pointer im #I pointer to the reference image
+pointer ls #I pointer to the linscale structure
+int rp #I index of the current region
+int max_nregions #I the maximum number of regions
+
+int i, istart, iend, j, jstart, jend, ncols, nlines, nxsample, nysample
+int nxcols, nylines, nregions
+pointer sp, region, section
+int rg_lstati(), nscan(), strcmp()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_LINE, TY_CHAR)
+ call salloc (section, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_lrealloc (ls, max_nregions)
+
+ # Initialize.
+ call rg_lstats (ls, REGIONS, Memc[region], SZ_LINE)
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+ nregions = min (rp - 1, rg_lstati (ls, NREGIONS))
+
+ # Decode the grid specification.
+ call sscan (Memc[region])
+ call gargwrd (Memc[section], SZ_LINE)
+ call gargi (nxsample)
+ call gargi (nysample)
+ if ((nscan() != 3) || (strcmp (Memc[section], "grid") != 0)) {
+ call sfree (sp)
+ return (nregions)
+ }
+
+ # Decode the regions.
+ if ((nxsample * nysample) > max_nregions) {
+ nxsample = nint (sqrt (real (max_nregions) * real (ncols) /
+ real (nlines)))
+ nysample = real (max_nregions) / real (nxsample)
+ }
+ nxcols = ncols / nxsample
+ nylines = nlines / nysample
+ jstart = 1 + (nlines - nysample * nylines) / 2
+ jend = jstart + (nysample - 1) * nylines
+ do j = jstart, jend, nylines {
+ istart = 1 + (ncols - nxsample * nxcols) / 2
+ iend = istart + (nxsample - 1) * nxcols
+ do i = istart, iend, nxcols {
+ Memi[rg_lstatp(ls,RC1)+nregions] = i
+ Memi[rg_lstatp(ls,RC2)+nregions] = i + nxcols - 1
+ Memi[rg_lstatp(ls,RL1)+nregions] = j
+ Memi[rg_lstatp(ls,RL2)+nregions] = j + nylines - 1
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = 1
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = 1
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+ }
+
+ call rg_lseti (ls, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_lrealloc (ls, nregions)
+ else
+ call rg_lrfree (ls)
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_LGREGIONS -- Compute the column and line limits givenan x and y
+# coordinate and a default size.
+
+int procedure rg_lgregions (im, ls, rp, max_nregions)
+
+pointer im #I pointer to the image
+pointer ls #I pointer to the linscale structure
+int rp #I pointer to the current region
+int max_nregions #I maximum number of regions
+
+char comma
+int ncols, nlines, nregions, onscan()
+int x1, x2, y1, y2
+pointer sp, region
+real x, y, xc, yc
+int rg_lstati(), nscan()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information.
+ call rg_lrealloc (ls, max_nregions)
+
+ # Get the constants.
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+
+ # Decode the center.
+ call rg_lstats (ls, REGIONS, Memc[region], SZ_LINE)
+ nregions = min (rp - 1, rg_lstati (ls, NREGIONS))
+ onscan = 0
+ call sscan (Memc[region])
+ call gargr (x)
+ call gargr (y)
+ call gargc (comma)
+
+ # Compute the data region.
+ while ((nscan() == onscan + 3) && (nregions < max_nregions)) {
+
+ # Check for the comma.
+ if (comma != ',')
+ break
+
+ # Compute a more accurate center.
+ #if (rg_lstati (ls, CENTER) == YES) {
+ #call rg_lcntr (im, x, y, DEF_CRADIUS, xc, yc)
+ #} else {
+ xc = x
+ yc = y
+ #}
+
+ # Compute the data section.
+ x1 = xc - rg_lstati (ls, DNX) / 2
+ x2 = x1 + rg_lstati (ls, DNX) - 1
+ if (IM_NDIM(im) == 1) {
+ y1 = 1
+ y2 = 1
+ } else {
+ y1 = yc - rg_lstati (ls, DNY) / 2
+ y2 = y1 + rg_lstati (ls, DNY) - 1
+ }
+
+ # Make sure that the region is on the image.
+ if (x1 >= 1 && x2 <= IM_LEN(im,1) && y1 >= 1 &&
+ y2 <= IM_LEN(im,2)) {
+ Memi[rg_lstatp(ls,RC1)+nregions] = x1
+ Memi[rg_lstatp(ls,RC2)+nregions] = x2
+ Memi[rg_lstatp(ls,RL1)+nregions] = y1
+ Memi[rg_lstatp(ls,RL2)+nregions] = y2
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = 1
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = 1
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+
+ onscan = nscan()
+ call gargr (x)
+ call gargr (y)
+ call gargc (comma)
+ }
+
+ # Reallocate the correct amount of space.
+ call rg_lseti (ls, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_lrealloc (ls, nregions)
+ else
+ call rg_lrfree (ls)
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_LMKREGIONS -- Procedure to mark the sections on the image display.
+# Sections are marked by pointing the image display cursor to the
+# lower left and upper rights corners of the desired sections respectively.
+
+int procedure rg_lmkregions (fd, im, ls, rp, max_nregions, regions, maxch)
+
+int fd #I pointer to the output text file
+pointer im #I pointer to the image
+pointer ls #I pointer to the intensity scaling structure
+int rp #I pointer to current region
+int max_nregions #I maximum number of regions
+char regions[ARB] #O the output regions string
+int maxch #I the maximum size of the output string
+
+int nregions, op, wcs, key
+pointer sp, cmd
+real xll, yll, xur, yur
+int rg_lstati(), clgcur(), gstrcpy()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_lrealloc (ls, max_nregions)
+
+ # Initialize.
+ nregions = min (rp-1, rg_lstati (ls, NREGIONS))
+ op = 1
+ regions[1] = EOS
+
+ while (nregions < max_nregions) {
+
+ call printf ("Mark lower left corner of region %d [q to quit].\n")
+ call pargi (nregions + 1)
+ if (clgcur ("icommands", xll, yll, wcs, key, Memc[cmd],
+ SZ_LINE) == EOF)
+ break
+ if (key == 'q')
+ break
+
+ call printf ("Mark upper right corner of region %d [q to quit].\n")
+ call pargi (nregions + 1)
+ if (clgcur ("icommands", xur, yur, wcs, key, Memc[cmd],
+ SZ_LINE) == EOF)
+ break
+ if (key == 'q')
+ break
+
+ # Make sure that the region is on the image.
+ if (xll < 1.0 || xur > IM_LEN(im,1) || yll < 1.0 || yur >
+ IM_LEN(im,2))
+ next
+
+ Memi[rg_lstatp(ls,RC1)+nregions] = nint(xll)
+ Memi[rg_lstatp(ls,RC2)+nregions] = nint(xur)
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = 1
+ Memi[rg_lstatp(ls,RL1)+nregions] = nint(yll)
+ Memi[rg_lstatp(ls,RL2)+nregions] = nint(yur)
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = 1
+
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = nregions + 1
+
+ # Write the regions string.
+ call sprintf (Memc[cmd], SZ_LINE, "[%d:%d,%d:%d] ")
+ call pargi (nint(xll))
+ call pargi (nint(xur))
+ call pargi (nint(yll))
+ call pargi (nint(yur))
+ op = op + gstrcpy (Memc[cmd], regions[op], maxch - op + 1)
+
+ # Write the output record.
+ if (fd != NULL) {
+ call fprintf (fd, "[%d:%d,%d:%d]\n")
+ call pargi (nint(xll))
+ call pargi (nint(xur))
+ call pargi (nint(yll))
+ call pargi (nint(yur))
+ }
+ }
+ call printf ("\n")
+
+ # Reallocate the correct amount of space.
+ call rg_lsets (ls, REGIONS, regions)
+ call rg_lseti (ls, NREGIONS, nregions)
+
+ if (nregions > 0)
+ call rg_lrealloc (ls, nregions)
+ else
+ call rg_lrfree (ls)
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_LMKXY -- Create a list of objects by selecting objects with
+# the image display cursor.
+
+int procedure rg_lmkxy (fd, im, ls, rp, max_nregions)
+
+int fd #I the output coordinates file descriptor
+pointer im #I pointer to the image
+pointer ls #I pointer to the psf matching structure
+int rp #I pointer to current region
+int max_nregions #I maximum number of regions
+
+int nregions, wcs, key, x1, x2, y1, y2
+pointer sp, region, cmd
+real xc, yc
+int clgcur(), rg_lstati()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_FNAME, TY_CHAR)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_lrealloc (ls, max_nregions)
+
+ nregions = min (rp-1, rg_lstati (ls, NREGIONS))
+ while (nregions < max_nregions) {
+
+ # Identify the object.
+ call printf ("Mark object %d [any key=mark,q=quit]:\n")
+ call pargi (nregions + 1)
+ if (clgcur ("icommands", xc, yc, wcs, key, Memc[cmd],
+ SZ_LINE) == EOF)
+ break
+ if (key == 'q')
+ break
+
+ # Compute the data section.
+ x1 = xc - rg_lstati (ls, DNX) / 2
+ x2 = x1 + rg_lstati (ls, DNX) - 1
+ y1 = yc - rg_lstati (ls, DNY) / 2
+ y2 = y1 + rg_lstati (ls, DNY) - 1
+
+ # Make sure that the region is on the image.
+ if (x1 < 1 || x2 > IM_LEN(im,1) || y1 < 1 || y2 >
+ IM_LEN(im,2))
+ next
+
+ if (fd != NULL) {
+ call fprintf (fd, "%0.3f %0.3f\n")
+ call pargr (xc)
+ call pargr (yc)
+ }
+
+ Memi[rg_lstatp(ls,RC1)+nregions] = x1
+ Memi[rg_lstatp(ls,RC2)+nregions] = x2
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = 1
+ Memi[rg_lstatp(ls,RL1)+nregions] = y1
+ Memi[rg_lstatp(ls,RL2)+nregions] = y2
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = 1
+
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+
+ nregions = nregions + 1
+
+ }
+
+ # Reallocate the correct amount of space.
+ call rg_lseti (ls, NREGIONS, nregions)
+ if (nregions > 0) {
+ call rg_lrealloc (ls, nregions)
+ if (fd != NULL) {
+ call fstats (fd, F_FILENAME, Memc[region], SZ_FNAME)
+ call rg_lsets (ls, REGIONS, Memc[region])
+ } else
+ call rg_lsets (ls, REGIONS, "")
+ } else {
+ call rg_lrfree (ls)
+ call rg_lsets (ls, REGIONS, "")
+ }
+
+ call sfree (sp)
+ return (nregions)
+end
+
+
+# RG_LRSECTIONS -- Read the sections from a file.
+
+int procedure rg_lrsections (fd, im, ls, rp, max_nregions)
+
+int fd #I the regions file descriptor
+pointer im #I pointer to the image
+pointer ls #I pointer to the linscale structure
+int rp #I pointer to current region
+int max_nregions #I the maximum number of regions
+
+int stat, nregions, ncols, nlines, x1, y1, x2, y2, xstep, ystep
+pointer sp, section, line
+int rg_lstati(), getline(), rg_lgsections()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (line, SZ_LINE, TY_CHAR)
+ call salloc (section, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_lrealloc (ls, max_nregions)
+
+ # Get the constants.
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+
+ # Decode the regions string.
+ nregions = min (rp - 1, rg_lstati (ls, NREGIONS))
+ while (getline (fd, Memc[line]) != EOF && nregions < max_nregions) {
+
+ call sscan (Memc[line])
+ call gargwrd (Memc[section], SZ_LINE)
+
+ while (Memc[section] != EOS && nregions < max_nregions) {
+ stat = rg_lgsections (Memc[section], x1, x2, xstep, y1, y2,
+ ystep, ncols, nlines)
+
+ # Check for even dimensioned regions.
+ if (stat == OK) {
+ if (mod (x2 - x1 + 1, 2) == 2) {
+ x2 = x2 + 1
+ if (x2 > ncols)
+ x2 = x2 - 2
+ if (x2 < 1)
+ stat = ERR
+ }
+ if (mod (y2 - y1 + 1, 2) == 2) {
+ y2 = y2 + 1
+ if (y2 > nlines)
+ y2 = y2 - 2
+ if (y2 < 1)
+ stat = ERR
+ }
+ } else
+ stat = ERR
+
+ # Add the new region to the list.
+ if (stat == OK) {
+ Memi[rg_lstatp(ls,RC1)+nregions] = x1
+ Memi[rg_lstatp(ls,RC2)+nregions] = x2
+ Memi[rg_lstatp(ls,RL1)+nregions] = y1
+ Memi[rg_lstatp(ls,RL2)+nregions] = y2
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = xstep
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = ystep
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+
+ call gargwrd (Memc[section], SZ_LINE)
+ }
+ }
+
+ call rg_lseti (ls, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_lrealloc (ls, nregions)
+ else
+ call rg_lrfree (ls)
+
+ call sfree (sp)
+ return (nregions)
+end
+
+
+# RG_LRCOORDS -- Read the coordinates from a file.
+
+int procedure rg_lrcoords (fd, im, ls, rp, max_nregions)
+
+int fd #I the regions file descriptor
+pointer im #I pointer to the image
+pointer ls #I pointer to the linscale structure
+int rp #I pointer to current region
+int max_nregions #I the maximum number of regions
+
+int ncols, nlines, nregions, x1, x2, y1, y2
+pointer sp, line
+real x, y, xc, yc
+int rg_lstati(), getline(), nscan()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (line, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_lrealloc (ls, max_nregions)
+
+ # Get the constants.
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+
+ # Decode the regions string.
+ nregions = min (rp - 1, rg_lstati (ls, NREGIONS))
+ while (getline (fd, Memc[line]) != EOF && nregions < max_nregions) {
+
+ call sscan (Memc[line])
+ call gargr (x)
+ call gargr (y)
+ if (nscan() != 2)
+ next
+
+ # Compute a more accurate center.
+ #if (rg_lstati (ls, CENTER) == YES) {
+ #call rg_lcntr (im, x, y, DEF_CRADIUS, xc, yc)
+ #} else {
+ xc = x
+ yc = y
+ #}
+
+ # Compute the data section.
+ x1 = xc - rg_lstati (ls, DNX) / 2
+ x2 = x1 + rg_lstati (ls, DNX) - 1
+ if (IM_NDIM(im) == 1) {
+ y1 = 1
+ y2 = 1
+ } else {
+ y1 = yc - rg_lstati (ls, DNY) / 2
+ y2 = y1 + rg_lstati (ls, DNY) - 1
+ }
+
+ # Make sure that the region is on the image.
+ if (x1 >= 1 && x2 <= IM_LEN(im,1) && y1 >= 1 && y2 <=
+ IM_LEN(im,2)) {
+ Memi[rg_lstatp(ls,RC1)+nregions] = x1
+ Memi[rg_lstatp(ls,RC2)+nregions] = x2
+ Memi[rg_lstatp(ls,RL1)+nregions] = y1
+ Memi[rg_lstatp(ls,RL2)+nregions] = y2
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = 1
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = 1
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+ }
+
+ call rg_lseti (ls, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_lrealloc (ls, nregions)
+ else
+ call rg_lrfree (ls)
+
+ call sfree (sp)
+ return (nregions)
+end
+
+
+# RG_LRPHOT -- Read the photometry from a file.
+
+int procedure rg_lrphot (fd, ls, rp, max_nregions, refimage)
+
+int fd #I the regions file descriptor
+pointer ls #I pointer to the linscale structure
+int rp #I pointer to current region
+int max_nregions #I the maximum number of regions
+int refimage #I is the photometry for the reference image
+
+int nregions, maxnr
+pointer sp, line
+real sky, skyerr, mag, magerr
+int rg_lstati(), getline(), nscan()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (line, SZ_LINE, TY_CHAR)
+
+ # Allocate the space to hold the arrays.
+ if (refimage == YES) {
+ call rg_lrealloc (ls, max_nregions)
+ nregions = min (rp - 1, rg_lstati (ls, NREGIONS))
+ maxnr = max_nregions
+ } else {
+ nregions = 0
+ maxnr = rg_lstati(ls, NREGIONS)
+ }
+
+ while (getline (fd, Memc[line]) != EOF && nregions < maxnr) {
+
+ call sscan (Memc[line])
+ call gargr (sky)
+ call gargr (skyerr)
+ call gargr (mag)
+ call gargr (magerr)
+ if (nscan() != 4)
+ next
+
+ Memi[rg_lstatp(ls,RC1)+nregions] = INDEFI
+ Memi[rg_lstatp(ls,RC2)+nregions] = INDEFI
+ Memi[rg_lstatp(ls,RL1)+nregions] = INDEFI
+ Memi[rg_lstatp(ls,RL2)+nregions] = INDEFI
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = INDEFI
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = INDEFI
+
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+ if (refimage == YES) {
+ Memr[rg_lstatp(ls,RSKY)+nregions] = sky
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = skyerr
+ Memr[rg_lstatp(ls,RMAG)+nregions] = mag
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = magerr
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ }
+
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+ if (refimage == NO) {
+ Memr[rg_lstatp(ls,ISKY)+nregions] = sky
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = skyerr
+ Memr[rg_lstatp(ls,IMAG)+nregions] = mag
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = magerr
+ }
+
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+
+ if (refimage == YES) {
+ call rg_lseti (ls, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_lrealloc (ls, nregions)
+ else
+ call rg_lrfree (ls)
+ } else if (nregions < rg_lstati (ls,NREGIONS)) {
+ call rg_lseti (ls, NREGIONS, nregions)
+ }
+
+ call sfree (sp)
+ return (nregions)
+end
+
+
+# RG_LSREGIONS -- Procedure to compute the column and line limits given
+# an image section. If the section is the null string then the region list
+# is empty.
+
+int procedure rg_lsregions (im, ls, rp, max_nregions)
+
+pointer im #I pointer to the image
+pointer ls #I pointer to the linscale structure
+int rp #I pointer to the current region
+int max_nregions #I maximum number of regions
+
+int ncols, nlines, nregions
+int x1, x2, y1, y2, xstep, ystep
+pointer sp, section, region
+int rg_lstati(), rg_lgsections()
+pointer rg_lstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_LINE, TY_CHAR)
+ call salloc (section, SZ_LINE, TY_CHAR)
+ call rg_lstats (ls, REGIONS, Memc[region], SZ_LINE)
+
+ # Allocate the arrays to hold the regions information.
+ call rg_lrealloc (ls, max_nregions)
+
+ # Get the constants.
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+
+ if (Memc[region] != EOS) {
+
+ call sscan (Memc[region])
+ call gargwrd (Memc[section], SZ_LINE)
+
+ nregions = min (rp - 1, rg_lstati (ls, NREGIONS))
+ while (Memc[section] != EOS && nregions < max_nregions) {
+
+ # Check for even dimensioned regions.
+ if (rg_lgsections (Memc[section], x1, x2, xstep, y1, y2, ystep,
+ ncols, nlines) == OK) {
+ Memi[rg_lstatp(ls,RC1)+nregions] = x1
+ Memi[rg_lstatp(ls,RC2)+nregions] = x2
+ Memi[rg_lstatp(ls,RL1)+nregions] = y1
+ Memi[rg_lstatp(ls,RL2)+nregions] = y2
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = xstep
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = ystep
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+ call gargwrd (Memc[section], SZ_LINE)
+ }
+
+ } else {
+ Memi[rg_lstatp(ls,RC1)+nregions] = 1
+ Memi[rg_lstatp(ls,RC2)+nregions] = ncols
+ Memi[rg_lstatp(ls,RL1)+nregions] = 1
+ Memi[rg_lstatp(ls,RL2)+nregions] = nlines
+ Memi[rg_lstatp(ls,RXSTEP)+nregions] = 1
+ Memi[rg_lstatp(ls,RYSTEP)+nregions] = 1
+ Memr[rg_lstatp(ls,RMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RSKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RNPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,IMEAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMEDIAN)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMODE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISIGMA)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKY)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,ISKYERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAG)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,IMAGERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,INPTS)+nregions] = INDEFI
+ Memr[rg_lstatp(ls,RBSCALE)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBSCALEERR)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZERO)+nregions] = INDEFR
+ Memr[rg_lstatp(ls,RBZEROERR)+nregions] = INDEFR
+ Memi[rg_lstatp(ls,RDELETE)+nregions] = LS_NO
+ Memr[rg_lstatp(ls,RCHI)+nregions] = INDEFR
+ nregions = 1
+ }
+
+
+ # Reallocate the correct amount of space.
+ call rg_lseti (ls, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_lrealloc (ls, nregions)
+ else
+ call rg_lrfree (ls)
+
+ call sfree (sp)
+ return (nregions)
+end
+
+
+# RG_LGSECTIONS -- Decode an image section into column and line limits
+# and a step size. Sections which describe the whole image are decoded into
+# a block ncols * nlines long.
+
+int procedure rg_lgsections (section, x1, x2, xstep, y1, y2, ystep, ncols,
+ nlines)
+
+char section[ARB] #I the input section string
+int x1, x2 #O the output column section limits
+int xstep #O the output column step size
+int y1, y2 #O the output line section limits
+int ystep #O the output line step size
+int ncols, nlines #I the maximum number of lines and columns
+
+int ip
+int rg_lgdim()
+
+begin
+ ip = 1
+ if (rg_lgdim (section, ip, x1, x2, xstep, ncols) == ERR)
+ return (ERR)
+ if (rg_lgdim (section, ip, y1, y2, ystep, nlines) == ERR)
+ return (ERR)
+
+ return (OK)
+end
+
+
+# RG_LGDIM -- Decode a single subscript expression to produce the
+# range of values for that subscript (X1:X2), and the sampling step size, STEP.
+# Note that X1 may be less than, greater than, or equal to X2, and STEP may
+# be a positive or negative nonzero integer. Various shorthand notations are
+# permitted, as is embedded whitespace.
+
+int procedure rg_lgdim (section, ip, x1, x2, step, limit)
+
+char section[ARB] #I the input image section
+int ip #I/O pointer to the position in section string
+int x1 #O first limit of dimension
+int x2 #O second limit of dimension
+int step #O step size of dimension
+int limit #I maximum size of dimension
+
+int temp
+int ctoi()
+
+begin
+ x1 = 1
+ x2 = limit
+ step = 1
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ if (section[ip] =='[')
+ ip = ip + 1
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+
+ # Get X1, X2.
+ if (ctoi (section, ip, temp) > 0) { # [x1
+ x1 = max (1, min (temp, limit))
+ if (section[ip] == ':') {
+ ip = ip + 1
+ if (ctoi (section, ip, temp) == 0) # [x1:x2
+ return (ERR)
+ x2 = max (1, min (temp, limit))
+ } else
+ x2 = x1
+
+ } else if (section[ip] == '-') {
+ x1 = limit
+ x2 = 1
+ ip = ip + 1
+ if (section[ip] == '*')
+ ip = ip + 1
+
+ } else if (section[ip] == '*') # [*
+ ip = ip + 1
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ # Get sample step size, if give.
+ if (section[ip] == ':') { # ..:step
+ ip = ip + 1
+ if (ctoi (section, ip, step) == 0)
+ return (ERR)
+ else if (step == 0)
+ return (ERR)
+ }
+
+ # Allow notation such as "-*:5", (or even "-:5") where the step
+ # is obviously supposed to be negative.
+
+ if (x1 > x2 && step > 0)
+ step = -step
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ if (section[ip] == ',') {
+ ip = ip + 1
+ return (OK)
+ } else if (section[ip] == ']')
+ return (OK)
+ else
+ return (ERR)
+end
+
+
+
diff --git a/pkg/images/immatch/src/linmatch/rglscale.x b/pkg/images/immatch/src/linmatch/rglscale.x
new file mode 100644
index 00000000..480455ea
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rglscale.x
@@ -0,0 +1,1337 @@
+include <imhdr.h>
+include <mach.h>
+include "linmatch.h"
+include "lsqfit.h"
+
+# RG_LSCALE -- Compute the scaling parameters required to match the
+# intensities of an image to a reference image.
+
+int procedure rg_lscale (imr, im1, db, dformat, ls)
+
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer db #I pointer to the database file
+int dformat #I write the output file in database format
+pointer ls #I pointer to the linscale structure
+
+pointer sp, image, imname
+real bscale, bzero, bserr, bzerr
+bool streq()
+int rg_lstati(), fscan(), nscan()
+
+#int i, nregions
+#int rg_isfit ()
+#pointer rg_istatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call rg_lstats (ls, IMAGE, Memc[image], SZ_FNAME)
+
+ # Initialize.
+ bscale = 1.0
+ bzero = 0.0
+
+ # Compute the average bscale and bzero for the image either by
+ # reading it from a file or by computing it directly from the
+ # data.
+
+ if (rg_lstati(ls, BZALGORITHM) == LS_FILE && rg_lstati (ls,
+ BSALGORITHM) == LS_FILE) {
+
+ # Read the results of a previous run from the database file or
+ # a simple text file.
+ if (dformat == YES) {
+ call rg_lfile (db, ls, bscale, bzero, bserr, bzerr)
+ } else {
+ if (fscan(db) != EOF) {
+ call gargwrd (Memc[imname], SZ_FNAME)
+ call gargr (bscale)
+ call gargr (bzero)
+ call gargr (bserr)
+ call gargr (bzerr)
+ if (! streq (Memc[image], Memc[imname]) || nscan() != 5) {
+ bscale = 1.0
+ bzero = 0.0
+ bserr = INDEFR
+ bzerr = INDEFR
+ }
+ } else {
+ bscale = 1.0
+ bzero = 0.0
+ bserr = INDEFR
+ bzerr = INDEFR
+ }
+ }
+
+ # Store the values.
+ call rg_lsetr (ls, TBSCALE, bscale)
+ call rg_lsetr (ls, TBZERO, bzero)
+ call rg_lsetr (ls, TBSCALEERR, bserr)
+ call rg_lsetr (ls, TBZEROERR, bzerr)
+
+ } else {
+
+ # Write out the algorithm parameters.
+ if (dformat == YES)
+ call rg_ldbparams (db, ls)
+
+ # Compute the individual scaling factors and their errors for
+ # all the regions and the average scaling factors and their
+ # errors.
+ call rg_scale (imr, im1, ls, bscale, bzero, bserr, bzerr, YES)
+
+ # Write out the results for the individual regions.
+ if (dformat == YES)
+ call rg_lwreg (db, ls)
+
+ # Write out the final scaling factors
+ if (dformat == YES)
+ call rg_ldbtscale (db, ls)
+ else {
+ call fprintf (db, "%s %g %g %g %g\n")
+ call pargstr (Memc[image])
+ call pargr (bscale)
+ call pargr (bzero)
+ call pargr (bserr)
+ call pargr (bzerr)
+ }
+ }
+
+ call sfree (sp)
+
+ return (NO)
+end
+
+
+# RG_SCALE -- Compute the scaling parameters for a list of regions.
+
+procedure rg_scale (imr, im1, ls, tbscale, tbzero, tbserr, tbzerr, refit)
+
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer ls #I pointer to the intensity matching structure
+real tbscale #O the average scaling parameter
+real tbzero #O the average offset parameter
+real tbserr #O the average error in the scaling parameter
+real tbzerr #O the average error in the offset parameter
+int refit #I recompute entire fit, otherwise recompute averages
+
+int i, nregions, ngood
+double sumbscale, sumbzero, sumwbscale, sumbserr, sumbzerr, sumwbzero, dw
+real bscale, bzero, bserr, bzerr, avbscale, avbzero, avbserr, avbzerr
+int rg_lstati(), rg_limget(), rg_lbszfit()
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ # Determine the number of regions.
+ nregions = rg_lstati (ls, NREGIONS)
+
+ # Initialize the statistics
+ sumbscale = 0.0d0
+ sumbserr = 0.0d0
+ sumwbscale = 0.0d0
+ sumbzero = 0.0d0
+ sumbzerr = 0.0d0
+ sumwbzero = 0.0d0
+ ngood = 0
+
+ # Loop over the regions.
+ do i = 1, nregions {
+
+ if (refit == YES) {
+
+ # Set the current region.
+ call rg_lseti (ls, CNREGION, i)
+
+ # Fetch the data for the given region and estimate the mean,
+ # median, mode, standard deviation, and number of points in
+ # each region, if this is required by the algorithm.
+ if (imr != NULL) {
+ if (rg_limget (ls, imr, im1, i) == ERR) {
+ call rg_lgmmm (ls, i)
+ next
+ } else
+ call rg_lgmmm (ls, i)
+ }
+
+ # Compute bscale and bzero and store the results in the
+ # internal arrays
+ if (rg_lbszfit (ls, i, bscale, bzero, bserr, bzerr) == ERR)
+ next
+
+ } else {
+ bscale = Memr[rg_lstatp(ls,RBSCALE)+i-1]
+ bzero = Memr[rg_lstatp(ls,RBZERO)+i-1]
+ bserr = Memr[rg_lstatp(ls,RBSCALEERR)+i-1]
+ bzerr = Memr[rg_lstatp(ls,RBZEROERR)+i-1]
+ }
+
+ # Accumulate the weighted sums of the scaling factors.
+ if (Memi[rg_lstatp(ls,RDELETE)+i-1] == LS_NO &&
+ ! IS_INDEFR(bserr) && ! IS_INDEFR(bzerr)) {
+
+ if (bserr <= 0.0)
+ dw = 1.0d0
+ else
+ dw = 1.0d0 / bserr ** 2
+ sumbscale = sumbscale + dw * bscale
+ sumbserr = sumbserr + dw * bscale * bscale
+ sumwbscale = sumwbscale + dw
+
+ if (bzerr <= 0.0)
+ dw = 1.0d0
+ else
+ dw = 1.0d0 / bzerr ** 2
+ sumbzero = sumbzero + dw * bzero
+ sumbzerr = sumbzerr + dw * bzero * bzero
+ sumwbzero = sumwbzero + dw
+
+ ngood = ngood + 1
+ }
+ }
+
+ # Compute the average scaling factors.
+ call rg_avstats (sumbscale, sumbzero, sumwbscale, sumwbzero, sumbserr,
+ sumbzerr, bserr, bserr, avbscale, avbzero, avbserr, avbzerr, ngood)
+
+ # Perform the rejection cycle.
+ if (ngood > 2 && rg_lstati(ls, NREJECT) > 0 &&
+ (! IS_INDEFR(rg_lstatr(ls,LOREJECT)) || ! IS_INDEFR(rg_lstatr(ls,
+ HIREJECT)))) {
+ call rg_ravstats (ls, sumbscale, sumbzero, sumwbscale, sumwbzero,
+ sumbserr, sumbzerr, bserr, bzerr, avbscale, avbzero, avbserr,
+ avbzerr, ngood)
+ }
+
+ # Compute the final scaling factors.
+ if (ngood > 1) {
+ call rg_lbszavg (ls, avbscale, avbzero, avbserr, avbzerr,
+ tbscale, tbzero, tbserr, tbzerr)
+ } else {
+ tbscale = avbscale
+ tbzero = avbzero
+ tbserr = avbserr
+ tbzerr = avbzerr
+ }
+
+ # Store the compute values.
+ call rg_lsetr (ls, TBSCALE, tbscale)
+ call rg_lsetr (ls, TBZERO, tbzero)
+ call rg_lsetr (ls, TBSCALEERR, tbserr)
+ call rg_lsetr (ls, TBZEROERR, tbzerr)
+end
+
+
+# RG_LIMGET -- Fetch the reference and input image data and compute the
+# statistics for a given region.
+
+int procedure rg_limget (ls, imr, im1, i)
+
+pointer ls #I pointer to the intensity scaling structure
+pointer imr #I pointer to reference image
+pointer im1 #I pointer to image
+int i #I the region id
+
+int stat, nrimcols, nrimlines, nimcols, nimlines, nrcols, nrlines, ncols
+int nlines, rc1, rc2, rl1, rl2, c1, c2, l1, l2, xstep, ystep, npts
+pointer sp, str, ibuf, rbuf, prc1, prc2, prxstep, prl1, prl2, prystep
+int rg_lstati(), rg_simget()
+pointer rg_lstatp()
+real rg_lstatr()
+
+#int c1, c2, l1, l2
+#int ncols, nlines, npts
+
+define nextregion_ 11
+
+begin
+ stat = OK
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Delete the data of the previous region if any.
+ rbuf = rg_lstatp (ls, RBUF)
+ if (rbuf != NULL)
+ call mfree (rbuf, TY_REAL)
+ rbuf = NULL
+ ibuf = rg_lstatp (ls, IBUF)
+ if (ibuf != NULL)
+ call mfree (ibuf, TY_REAL)
+ ibuf = NULL
+
+ # Check for number of regions.
+ if (i < 1 || i > rg_lstati (ls, NREGIONS)) {
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Get the reference and input image sizes.
+ nrimcols = IM_LEN(imr,1)
+ if (IM_NDIM(imr) == 1)
+ nrimlines = 1
+ else
+ nrimlines = IM_LEN(imr,2)
+ nimcols = IM_LEN(im1,1)
+ if (IM_NDIM(im1) == 1)
+ nimlines = 1
+ else
+ nimlines = IM_LEN(im1,2)
+
+ # Get the reference region pointers.
+ prc1 = rg_lstatp (ls, RC1)
+ prc2 = rg_lstatp (ls, RC2)
+ prl1 = rg_lstatp (ls, RL1)
+ prl2 = rg_lstatp (ls, RL2)
+ prxstep = rg_lstatp (ls, RXSTEP)
+ prystep = rg_lstatp (ls, RYSTEP)
+
+ # Get the reference subraster regions.
+ rc1 = Memi[prc1+i-1]
+ rc2 = Memi[prc2+i-1]
+ rl1 = Memi[prl1+i-1]
+ rl2 = Memi[prl2+i-1]
+ xstep = Memi[prxstep+i-1]
+ ystep = Memi[prystep+i-1]
+ nrcols = (rc2 - rc1) / xstep + 1
+ nrlines = (rl2 - rl1) / ystep + 1
+
+ # Move to the next region if current reference region is off the image.
+ if (rc1 < 1 || rc1 > nrimcols || rc2 < 1 || rc2 > nrimcols ||
+ rl1 > nrimlines || rl1 < 1 || rl2 < 1 || rl2 > nrimlines) {
+ call rg_lstats (ls, REFIMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Reference region %d: %s[%d:%d:%d,%d:%d:%d] is off image.\n")
+ call pargi (i)
+ call pargstr (Memc[str])
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (xstep)
+ call pargi (rl1)
+ call pargi (rl2)
+ call pargi (ystep)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Move to next region if current reference region is too small.
+ if (nrcols < 3 || (IM_NDIM(imr) == 2 && nrlines < 3)) {
+ call rg_lstats (ls, REFIMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Reference region %d: %s[%d:%d:%d,%d:%d:%d] has too few points.\n")
+ call pargi (i)
+ call pargstr (Memc[str])
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (xstep)
+ call pargi (rl1)
+ call pargi (rl2)
+ call pargi (ystep)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Get the reference image data.
+ npts = rg_simget (imr, rc1, rc2, xstep, rl1, rl2, ystep, rbuf)
+ if (npts < 9) {
+ stat = ERR
+ go to nextregion_
+ }
+ call rg_lsetp (ls, RBUF, rbuf)
+ Memi[rg_lstatp(ls,RNPTS)+i-1] = npts
+
+ # Get the input image subraster regions.
+ c1 = rc1 + rg_lstatr (ls, SXSHIFT)
+ c2 = rc2 + rg_lstatr (ls, SXSHIFT)
+ l1 = rl1 + rg_lstatr (ls, SYSHIFT)
+ l2 = rl2 + rg_lstatr (ls, SYSHIFT)
+ #c1 = max (1, min (nimcols, c1))
+ #c2 = min (nimcols, max (1, c2))
+ #l1 = max (1, min (nimlines, l1))
+ #l2 = min (nimlines, max (1, l2))
+ ncols = (c2 - c1) / xstep + 1
+ nlines = (l2 - l1) / ystep + 1
+
+ # Move to the next region if current input region is off the image.
+ if (c1 < 1 || c1 > nimcols || c2 > nimcols || c2 < 1 ||
+ l1 > nimlines || l1 < 1 || l2 < 1 || l2 > nimlines) {
+ call rg_lstats (ls, IMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Input region %d: %s[%d:%d:%d,%d:%d:%d] is off image.\n")
+ call pargi (i)
+ call pargstr (Memc[str])
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (xstep)
+ call pargi (l1)
+ call pargi (l2)
+ call pargi (ystep)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Move to the next region if current input region is too small.
+ if (ncols < 3 || (IM_NDIM(im1) == 2 && nlines < 3)) {
+ call rg_lstats (ls, IMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Input regions %d: %s[%d:%d:%d,%d:%d:%d] has too few points.\n")
+ call pargi (i)
+ call pargstr (Memc[str])
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (xstep)
+ call pargi (l1)
+ call pargi (l2)
+ call pargi (ystep)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Get the image data.
+ npts = rg_simget (im1, c1, c2, xstep, l1, l2, ystep, ibuf)
+ if (npts < 9) {
+ stat = ERR
+ go to nextregion_
+ }
+ call rg_lsetp (ls, IBUF, ibuf)
+ Memi[rg_lstatp(ls,INPTS)+i-1] = npts
+
+
+nextregion_
+ call sfree (sp)
+ if (stat == ERR) {
+ call rg_lsetp (ls, RBUF, rbuf)
+ if (ibuf != NULL)
+ call mfree (ibuf, TY_REAL)
+ call rg_lsetp (ls, IBUF, NULL)
+ call rg_lseti (ls, CNREGION, i)
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADREGION
+ return (ERR)
+ } else {
+ call rg_lsetp (ls, RBUF, rbuf)
+ call rg_lsetp (ls, IBUF, ibuf)
+ call rg_lseti (ls, CNREGION, i)
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_NO
+ return (OK)
+ }
+end
+
+
+# RG_LGMMM -- Compute the mean, median and mode of a data region
+
+procedure rg_lgmmm (ls, i)
+
+pointer ls #I pointer to the intensity scaling structure
+int i #I the current region
+
+int npts
+pointer rbuf, ibuf, buf
+real sigma, dmin, dmax
+int rg_lstati()
+pointer rg_lstatp()
+real rg_lmode(), rg_lstatr()
+
+begin
+ # Test that the data buffers exist and contain data.
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+ npts = Memi[rg_lstatp (ls, RNPTS)+i-1]
+ if (rbuf == NULL || npts <= 0) {
+ Memr[rg_lstatp(ls,RMEAN)+i-1] = 0.0
+ Memr[rg_lstatp(ls,RMEDIAN)+i-1] = 0.0
+ Memr[rg_lstatp(ls,RMODE)+i-1] = 0.0
+ Memr[rg_lstatp(ls,RSIGMA)+i-1] = 0.0
+ Memr[rg_lstatp(ls,IMEAN)+i-1] = 0.0
+ Memr[rg_lstatp(ls,IMEDIAN)+i-1] = 0.0
+ Memr[rg_lstatp(ls,IMODE)+i-1] = 0.0
+ Memr[rg_lstatp(ls,ISIGMA)+i-1] = 0.0
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADREGION
+ return
+ }
+ call malloc (buf, npts, TY_REAL)
+
+ # Compute the mean, median, and mode of the reference region but
+ # don't recompute the reference region statistics needlessly.
+ if ((!IS_INDEFR(rg_lstatr(ls,DATAMIN)) || !IS_INDEFR(rg_lstatr(ls,
+ DATAMAX))) && (rg_lstati(ls,BSALGORITHM) != LS_FIT ||
+ rg_lstati(ls,BZALGORITHM) != LS_FIT)) {
+ call alimr (Memr[rbuf], npts, dmin, dmax)
+ if (!IS_INDEFR(rg_lstatr(ls,DATAMIN))) {
+ if (dmin < rg_lstatr(ls,DATAMIN)) {
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADREGION
+ call eprintf (
+ "Reference region %d contains data < datamin\n")
+ call pargi (i)
+ }
+ }
+ if (!IS_INDEFR(rg_lstatr(ls,DATAMAX))) {
+ if (dmax > rg_lstatr(ls,DATAMAX)) {
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADREGION
+ call eprintf (
+ "Reference region %d contains data > datamax\n")
+ call pargi (i)
+ }
+ }
+ }
+ call aavgr (Memr[rbuf], npts, Memr[rg_lstatp(ls,RMEAN)+i-1], sigma)
+ Memr[rg_lstatp(ls,RSIGMA)+i-1] = sigma / sqrt (real(npts))
+ call asrtr (Memr[rbuf], Memr[buf], npts)
+ if (mod (npts,2) == 1)
+ Memr[rg_lstatp(ls,RMEDIAN)+i-1] = Memr[buf+npts/2]
+ else
+ Memr[rg_lstatp(ls,RMEDIAN)+i-1] = (Memr[buf+npts/2-1] +
+ Memr[buf+npts/2]) / 2.0
+ Memr[rg_lstatp(ls,RMODE)+i-1] = rg_lmode (Memr[buf], npts,
+ LMODE_NMIN, LMODE_ZRANGE, LMODE_ZBIN, LMODE_ZSTEP)
+ sigma = sqrt ((max (Memr[rg_lstatp(ls,RMEAN)+i-1], 0.0) /
+ rg_lstatr(ls,RGAIN) + (rg_lstatr(ls,RREADNOISE) /
+ rg_lstatr (ls,RGAIN)) ** 2) / npts)
+ Memr[rg_lstatp(ls,RSIGMA)+i-1] =
+ min (Memr[rg_lstatp(ls,RSIGMA)+i-1], sigma)
+
+ if (ibuf == NULL) {
+ Memr[rg_lstatp(ls,IMEAN)+i-1] = Memr[rg_lstatp(ls,RMEAN)+i-1]
+ Memr[rg_lstatp(ls,IMEDIAN)+i-1] = Memr[rg_lstatp(ls,RMEDIAN)+i-1]
+ Memr[rg_lstatp(ls,IMODE)+i-1] = Memr[rg_lstatp(ls,RMODE)+i-1]
+ Memr[rg_lstatp(ls,ISIGMA)+i-1] = Memr[rg_lstatp(ls,RSIGMA)+i-1]
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADREGION
+ call mfree (buf, TY_REAL)
+ return
+ }
+
+ # Compute the mean, median, and mode of the input region.
+ if ((!IS_INDEFR(rg_lstatr(ls,DATAMIN)) || !IS_INDEFR(rg_lstatr(ls,
+ DATAMAX))) && (rg_lstati(ls,BSALGORITHM) != LS_FIT ||
+ rg_lstati(ls,BZALGORITHM) != LS_FIT)) {
+ call alimr (Memr[ibuf], npts, dmin, dmax)
+ if (!IS_INDEFR(rg_lstatr(ls,DATAMIN))) {
+ if (dmin < rg_lstatr(ls,DATAMIN)) {
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADREGION
+ call eprintf ("Input region %d contains data < datamin\n")
+ call pargi (i)
+ }
+ }
+ if (!IS_INDEFR(rg_lstatr(ls,DATAMAX))) {
+ if (dmax > rg_lstatr(ls,DATAMAX)) {
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADREGION
+ call eprintf ("Input region %d contains data > datamax\n")
+ call pargi (i)
+ }
+ }
+ }
+ call aavgr (Memr[ibuf], npts, Memr[rg_lstatp(ls,IMEAN)+i-1], sigma)
+ Memr[rg_lstatp(ls,ISIGMA)+i-1] = sigma / sqrt (real(npts))
+ call asrtr (Memr[ibuf], Memr[buf], npts)
+ if (mod (npts,2) == 1)
+ Memr[rg_lstatp(ls,IMEDIAN)+i-1] = Memr[buf+npts/2]
+ else
+ Memr[rg_lstatp(ls,IMEDIAN)+i-1] = (Memr[buf+npts/2-1] +
+ Memr[buf+npts/2]) / 2.0
+ Memr[rg_lstatp(ls,IMODE)+i-1] = rg_lmode (Memr[buf], npts, LMODE_NMIN,
+ LMODE_ZRANGE, LMODE_ZBIN, LMODE_ZSTEP)
+ sigma = sqrt ((max (Memr[rg_lstatp(ls,IMEAN)+i-1], 0.0) /
+ rg_lstatr(ls,IGAIN) + (rg_lstatr(ls,IREADNOISE) /
+ rg_lstatr (ls,IGAIN)) ** 2) / npts)
+ Memr[rg_lstatp(ls,ISIGMA)+i-1] =
+ min (Memr[rg_lstatp(ls,ISIGMA)+i-1], sigma)
+
+
+ call mfree (buf, TY_REAL)
+end
+
+
+# RG_LBSZFIT -- Compute the bscale and bzero factor for a single region.
+
+int procedure rg_lbszfit (ls, i, bscale, bzero, bserr, bzerr)
+
+pointer ls #I pointer to the intensity scaling strucuture
+int i #I the number of the current region
+real bscale #O the computed bscale factor
+real bzero #O the computed bzero factor
+real bserr #O the computed error in bscale
+real bzerr #O the computed error in bzero
+
+int stat
+real bjunk, chi
+bool fp_equalr()
+int rg_lstati()
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ stat = OK
+
+ # Compute the bscale factor.
+ switch (rg_lstati (ls, BSALGORITHM)) {
+ case LS_NUMBER:
+ bscale = rg_lstatr (ls, CBSCALE)
+ bserr = 0.0
+ chi = INDEFR
+ case LS_MEAN:
+ if (fp_equalr (0.0, Memr[rg_lstatp(ls,IMEAN)+i-1])) {
+ bscale = 1.0
+ bserr = 0.0
+ } else {
+ bscale = Memr[rg_lstatp(ls, RMEAN)+i-1] /
+ Memr[rg_lstatp (ls, IMEAN)+i-1]
+ if (fp_equalr (0.0, Memr[rg_lstatp(ls,RMEAN)+i-1]))
+ bserr = 0.0
+ else
+ bserr = abs (bscale) * sqrt ((Memr[rg_lstatp(ls,
+ RSIGMA)+i-1] / Memr[rg_lstatp(ls,RMEAN)+i-1]) ** 2 +
+ (Memr[rg_lstatp(ls, ISIGMA)+i-1] /
+ Memr[rg_lstatp(ls,IMEAN)+i-1]) ** 2)
+ }
+ chi = INDEFR
+ case LS_MEDIAN:
+ if (fp_equalr (0.0, Memr[rg_lstatp(ls,IMEDIAN)+i-1])) {
+ bscale = 1.0
+ bserr= 0.0
+ } else {
+ bscale = Memr[rg_lstatp (ls,RMEDIAN)+i-1] /
+ Memr[rg_lstatp(ls,IMEDIAN)+i-1]
+ if (fp_equalr (0.0, Memr[rg_lstatp(ls,RMEDIAN)+i-1]))
+ bserr = 0.0
+ else
+ bserr = abs (bscale) * sqrt ((Memr[rg_lstatp(ls,
+ RSIGMA)+i-1] / Memr[rg_lstatp(ls,RMEDIAN)+i-1]) ** 2 +
+ (Memr[rg_lstatp(ls, ISIGMA)+i-1] / Memr[rg_lstatp(ls,
+ IMEDIAN)+i-1]) ** 2)
+ }
+ chi = INDEFR
+ case LS_MODE:
+ if (fp_equalr (0.0, Memr[rg_lstatp (ls,IMODE)+i-1])) {
+ bscale = 1.0
+ bserr = 0.0
+ } else {
+ bscale = Memr[rg_lstatp (ls, RMODE)+i-1] /
+ Memr[rg_lstatp (ls, IMODE)+i-1]
+ if (fp_equalr (0.0, Memr[rg_lstatp (ls,RMODE)+i-1]))
+ bserr = 0.0
+ else
+ bserr = abs (bscale) * sqrt ((Memr[rg_lstatp(ls,
+ RSIGMA)+i-1] / Memr[rg_lstatp(ls,RMODE)+i-1]) ** 2 +
+ (Memr[rg_lstatp(ls, ISIGMA)+i-1] / Memr[rg_lstatp(ls,
+ IMODE)+i-1]) ** 2)
+ }
+ chi = INDEFR
+ case LS_FIT:
+ call rg_llsqfit (ls, i, bscale, bzero, bserr, bzerr, chi)
+ case LS_PHOTOMETRY:
+ if (IS_INDEFR(Memr[rg_lstatp(ls,RMAG)+i-1]) ||
+ IS_INDEFR(Memr[rg_lstatp(ls,IMAG)+i-1])) {
+ bscale = 1.0
+ bserr = 0.0
+ } else {
+ bscale = 10.0 ** ((Memr[rg_lstatp(ls,IMAG)+i-1] -
+ Memr[rg_lstatp(ls,RMAG)+i-1]) / 2.5)
+ if (IS_INDEFR(Memr[rg_lstatp(ls,RMAGERR)+i-1]) ||
+ IS_INDEFR(Memr[rg_lstatp(ls,IMAGERR)+i-1]))
+ bserr = 0.0
+ else
+ bserr = 0.4 * log (10.0) * bscale *
+ sqrt (Memr[rg_lstatp(ls,RMAGERR)+i-1] ** 2 +
+ Memr[rg_lstatp(ls,IMAGERR)+i-1] ** 2)
+ }
+ chi = INDEFR
+ default:
+ bscale = 1.0
+ bserr = 0.0
+ chi = INDEFR
+ }
+
+ # Compute the bzero factor.
+ switch (rg_lstati (ls, BZALGORITHM)) {
+ case LS_NUMBER:
+ bzero = rg_lstatr (ls, CBZERO)
+ bzerr = 0.0
+ chi = INDEFR
+ case LS_MEAN:
+ if (rg_lstati(ls, BSALGORITHM) == LS_NUMBER) {
+ bzero = Memr[rg_lstatp(ls,RMEAN)+i-1] - Memr[rg_lstatp(ls,
+ IMEAN)+i-1]
+ bzerr = sqrt (Memr[rg_lstatp(ls,RSIGMA)+i-1] ** 2 +
+ Memr[rg_lstatp(ls,ISIGMA)+i-1] ** 2)
+ } else {
+ bzero = 0.0
+ bzerr = 0.0
+ }
+ chi = INDEFR
+ case LS_MEDIAN:
+ if (rg_lstati(ls, BSALGORITHM) == LS_NUMBER) {
+ bzero = Memr[rg_lstatp(ls,RMEDIAN)+i-1] -
+ Memr[rg_lstatp(ls,IMEDIAN)+i-1]
+ bzerr = sqrt (Memr[rg_lstatp(ls,RSIGMA)+i-1] ** 2 +
+ Memr[rg_lstatp(ls,ISIGMA)+i-1] ** 2)
+ } else {
+ bzero = 0.0
+ bzerr = 0.0
+ }
+ chi = INDEFR
+ case LS_MODE:
+ if (rg_lstati(ls, BSALGORITHM) == LS_NUMBER) {
+ bzero = Memr[rg_lstatp(ls,RMODE)+i-1] - Memr[rg_lstatp(ls,
+ IMODE)+i-1]
+ bzerr = sqrt (Memr[rg_lstatp(ls,RSIGMA)+i-1] ** 2 +
+ Memr[rg_lstatp(ls,ISIGMA)+i-1] ** 2)
+ } else {
+ bzero = 0.0
+ bzerr = 0.0
+ }
+ chi = INDEFR
+ case LS_FIT:
+ if (rg_lstati(ls, BSALGORITHM) == LS_NUMBER)
+ call rg_llsqfit (ls, i, bjunk, bzero, bjunk, bzerr, chi)
+ case LS_PHOTOMETRY:
+ if (IS_INDEFR(Memr[rg_lstatp(ls,RSKY)+i-1]) ||
+ IS_INDEFR(Memr[rg_lstatp(ls,ISKY)+i-1])) {
+ bzero = 0.0
+ bzerr = 0.0
+ } else {
+ bzero = Memr[rg_lstatp(ls,RSKY)+i-1] - bscale *
+ Memr[rg_lstatp(ls,ISKY)+i-1]
+ if (IS_INDEFR(Memr[rg_lstatp(ls,RSKYERR)+i-1]) ||
+ IS_INDEFR(Memr[rg_lstatp(ls,ISKYERR)+i-1]))
+ bzerr = 0.0
+ else
+ bzerr = sqrt (Memr[rg_lstatp(ls,RSKYERR)+i-1] ** 2 +
+ bserr ** 2 * Memr[rg_lstatp(ls,ISKY)+i-1] ** 2 +
+ bscale ** 2 * Memr[rg_lstatp(ls,ISKYERR)+i-1] ** 2)
+
+ }
+ chi = INDEFR
+ default:
+ bzero = 0.0
+ bzerr = 0.0
+ chi = INDEFR
+ }
+
+ # Store the results.
+ Memr[rg_lstatp(ls,RBSCALE)+i-1] = bscale
+ Memr[rg_lstatp(ls,RBZERO)+i-1] = bzero
+ Memr[rg_lstatp(ls,RBSCALEERR)+i-1] = bserr
+ Memr[rg_lstatp(ls,RBZEROERR)+i-1] = bzerr
+ Memr[rg_lstatp(ls,RCHI)+i-1] = chi
+
+ return (stat)
+end
+
+
+# RG_LBSZAVG -- Compute the final scaling parameters.
+
+procedure rg_lbszavg (ls, avbscale, avbzero, avbserr, avbzerr, tbscale,
+ tbzero, tbserr, tbzerr)
+
+pointer ls #I pointer to the intensity scaling strucuture
+real avbscale #I the computed bscale factor
+real avbzero #I the computed bzero factor
+real avbserr #I the computed error in bscale
+real avbzerr #I the computed error in bzero
+real tbscale #O the computed bscale factor
+real tbzero #O the computed bzero factor
+real tbserr #O the computed error in bscale
+real tbzerr #O the computed error in bzero
+
+int i, bsalg, bzalg, nregions
+pointer sp, weight
+real answers[MAX_NFITPARS]
+int rg_lstati()
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ bsalg = rg_lstati (ls, BSALGORITHM)
+ bzalg = rg_lstati (ls, BZALGORITHM)
+ nregions = rg_lstati (ls, NREGIONS)
+
+ call smark (sp)
+ call salloc (weight, nregions, TY_REAL)
+
+ if (bsalg == LS_MEAN || bzalg == LS_MEAN) {
+ do i = 1, nregions {
+ if (IS_INDEFR(Memr[rg_lstatp(ls,IMEAN)+i-1]) ||
+ IS_INDEFR(Memr[rg_lstatp(ls,RMEAN)+i-1]) ||
+ Memi[rg_lstatp(ls,RDELETE)+i-1] != LS_NO)
+ Memr[weight+i-1] = 0.0
+ else
+ Memr[weight+i-1] = 1.0
+ }
+ call ll_lsqf1 (Memr[rg_lstatp(ls,IMEAN)], Memr[rg_lstatp(ls,
+ RMEAN)], Memr[rg_lstatp(ls,ISIGMA)], Memr[rg_lstatp(ls,
+ RSIGMA)], Memr[weight], nregions, rg_lstati(ls,MAXITER),
+ answers)
+ if (nregions > 2 && rg_lstati(ls,NREJECT) > 0 &&
+ (! IS_INDEFR(rg_lstatr(ls,LOREJECT)) ||
+ ! IS_INDEFR(rg_lstatr(ls,HIREJECT)))) {
+ call ll_rlsqf1 (Memr[rg_lstatp(ls,IMEAN)], Memr[rg_lstatp(ls,
+ RMEAN)], Memr[rg_lstatp(ls,ISIGMA)], Memr[rg_lstatp(ls,
+ RSIGMA)], Memr[weight], nregions, rg_lstati(ls,MAXITER),
+ answers, rg_lstati(ls,NREJECT), rg_lstatr(ls,LOREJECT),
+ rg_lstatr(ls,HIREJECT))
+ do i = 1, nregions {
+ if (Memr[weight+i-1] <= 0.0 && Memi[rg_lstatp(ls,
+ RDELETE)+i-1] == LS_NO)
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADSIGMA
+ }
+ }
+ if (IS_INDEFR(CHI[answers])) {
+ tbscale = avbscale
+ tbserr = avbserr
+ tbzero = avbzero
+ tbzerr = avbzerr
+ } else if (bsalg == LS_MEAN && bzalg == LS_MEAN) {
+ tbscale = SLOPE[answers]
+ tbserr = ESLOPE[answers]
+ tbzero = YINCPT[answers]
+ tbzerr = EYINCPT[answers]
+ } else if (bsalg == LS_MEAN) {
+ tbscale = SLOPE[answers]
+ tbserr = ESLOPE[answers]
+ tbzero = avbzero
+ tbzerr = avbzerr
+ } else {
+ tbscale = avbscale
+ tbserr = avbserr
+ tbzero = avbzero
+ tbzerr = avbzerr
+ }
+
+ } else if (bsalg == LS_MEDIAN || bzalg == LS_MEDIAN) {
+ do i = 1, nregions {
+ if (IS_INDEFR(Memr[rg_lstatp(ls,IMEDIAN)+i-1]) ||
+ IS_INDEFR(Memr[rg_lstatp(ls,RMEDIAN)+i-1]) ||
+ Memi[rg_lstatp(ls,RDELETE)+i-1] != LS_NO)
+ Memr[weight+i-1] = 0.0
+ else
+ Memr[weight+i-1] = 1.0
+ }
+ call ll_lsqf1 (Memr[rg_lstatp(ls,IMEDIAN)], Memr[rg_lstatp(ls,
+ RMEDIAN)], Memr[rg_lstatp(ls,ISIGMA)], Memr[rg_lstatp(ls,
+ RSIGMA)], Memr[weight], nregions, rg_lstati(ls,MAXITER),
+ answers)
+ if (nregions > 2 && rg_lstati(ls,NREJECT) > 0 &&
+ (! IS_INDEFR(rg_lstatr(ls,LOREJECT)) ||
+ ! IS_INDEFR(rg_lstatr(ls,HIREJECT)))) {
+ call ll_rlsqf1 (Memr[rg_lstatp(ls,IMEDIAN)], Memr[rg_lstatp(ls,
+ RMEDIAN)], Memr[rg_lstatp(ls,ISIGMA)], Memr[rg_lstatp(ls,
+ RSIGMA)], Memr[weight], nregions, rg_lstati(ls,MAXITER),
+ answers, rg_lstati(ls,NREJECT), rg_lstatr(ls,LOREJECT),
+ rg_lstatr(ls,HIREJECT))
+ do i = 1, nregions {
+ if (Memr[weight+i-1] <= 0.0 && Memi[rg_lstatp(ls,
+ RDELETE)+i-1] == LS_NO)
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADSIGMA
+ }
+ }
+ if (IS_INDEFR(CHI[answers])) {
+ tbscale = avbscale
+ tbserr = avbserr
+ tbzero = avbzero
+ tbzerr = avbzerr
+ } else if (bsalg == LS_MEDIAN && bzalg == LS_MEDIAN) {
+ tbscale = SLOPE[answers]
+ tbserr = ESLOPE[answers]
+ tbzero = YINCPT[answers]
+ tbzerr = EYINCPT[answers]
+ } else if (bsalg == LS_MEDIAN) {
+ tbscale = SLOPE[answers]
+ tbserr = ESLOPE[answers]
+ tbzero = avbzero
+ tbzerr = avbzerr
+ } else {
+ tbscale = avbscale
+ tbserr = avbserr
+ tbzero = avbzero
+ tbzerr = avbzerr
+ }
+ } else if (bsalg == LS_MODE || bzalg == LS_MODE) {
+ do i = 1, nregions {
+ if (IS_INDEFR(Memr[rg_lstatp(ls,IMODE)+i-1]) ||
+ IS_INDEFR(Memr[rg_lstatp(ls,RMODE)+i-1]) ||
+ Memi[rg_lstatp(ls,RDELETE)+i-1] != LS_NO)
+ Memr[weight+i-1] = 0.0
+ else
+ Memr[weight+i-1] = 1.0
+ }
+ call ll_lsqf1 (Memr[rg_lstatp(ls,IMODE)], Memr[rg_lstatp(ls,
+ RMODE)], Memr[rg_lstatp(ls,ISIGMA)], Memr[rg_lstatp(ls,
+ RSIGMA)], Memr[weight], nregions, rg_lstati(ls,MAXITER),
+ answers)
+ if (nregions > 2 && rg_lstati(ls,NREJECT) > 0 &&
+ (! IS_INDEFR(rg_lstatr(ls,LOREJECT)) ||
+ ! IS_INDEFR(rg_lstatr(ls,HIREJECT)))) {
+ call ll_rlsqf1 (Memr[rg_lstatp(ls,IMODE)], Memr[rg_lstatp(ls,
+ RMODE)], Memr[rg_lstatp(ls,ISIGMA)], Memr[rg_lstatp(ls,
+ RSIGMA)], Memr[weight], nregions, rg_lstati(ls,MAXITER),
+ answers, rg_lstati(ls,NREJECT), rg_lstatr(ls,LOREJECT),
+ rg_lstatr(ls,HIREJECT))
+ do i = 1, nregions {
+ if (Memr[weight+i-1] <= 0.0 && Memi[rg_lstatp(ls,
+ RDELETE)+i-1] == LS_NO)
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADSIGMA
+ }
+ }
+ if (IS_INDEFR(CHI[answers])) {
+ tbscale = avbscale
+ tbserr = avbserr
+ tbzero = avbzero
+ tbzerr = avbzerr
+ } else if (bsalg == LS_MODE && bzalg == LS_MODE) {
+ tbscale = SLOPE[answers]
+ tbserr = ESLOPE[answers]
+ tbzero = YINCPT[answers]
+ tbzerr = EYINCPT[answers]
+ } else if (bsalg == LS_MODE) {
+ tbscale = SLOPE[answers]
+ tbserr = ESLOPE[answers]
+ tbzero = avbzero
+ tbzerr = avbzerr
+ } else {
+ tbscale = avbscale
+ tbserr = avbserr
+ tbzero = avbzero
+ tbzerr = avbzerr
+ }
+ } else {
+ tbscale = avbscale
+ tbzero = avbzero
+ tbserr = avbserr
+ tbzerr = avbzerr
+ }
+
+
+ call sfree (sp)
+end
+
+
+# RG_LFILE -- Fetch the scaling parameters from the datafile.
+
+procedure rg_lfile (db, ls, bscale, bzero, bserr, bzerr)
+
+pointer db #I pointer to the database file
+pointer ls #I pointer to the intensity scaling structure
+real bscale #O the average scaling parameter
+real bzero #O the average offset parameter
+real bserr #O the error in bscale
+real bzerr #O the error in bzero
+
+int rec
+pointer sp, record
+int dtlocate()
+real dtgetr()
+
+begin
+ call smark (sp)
+ call salloc (record, SZ_FNAME, TY_CHAR)
+
+ call rg_lstats (ls, RECORD, Memc[record], SZ_FNAME)
+ iferr {
+ rec = dtlocate (db, Memc[record])
+ bscale = dtgetr (db, rec, "bscale")
+ bzero = dtgetr (db, rec, "bzero")
+ bserr = dtgetr (db, rec, "bserr")
+ bzerr = dtgetr (db, rec, "bzerr")
+ } then {
+ bscale = 1.0
+ bzero = 0.0
+ bserr = INDEFR
+ bzerr = INDEFR
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_SIMGET -- Fill a buffer from a specified region of the image including a
+# step size in x and y.
+
+int procedure rg_simget (im, c1, c2, cstep, l1, l2, lstep, ptr)
+
+pointer im #I the pointer to the iraf image
+int c1, c2 #I the column limits
+int cstep #I the column step size
+int l1, l2 #I the line limits
+int lstep #I the line step size
+pointer ptr #I the pointer to the output buffer
+
+int i, j, ncols, nlines, npts
+pointer iptr, buf
+pointer imgs2r()
+
+begin
+ ncols = (c2 - c1) / cstep + 1
+ nlines = (l2 - l1) / lstep + 1
+ npts = ncols * nlines
+ call malloc (ptr, npts, TY_REAL)
+
+ iptr = ptr
+ do j = l1, l2, lstep {
+ buf = imgs2r (im, c1, c2, j, j)
+ do i = 1, ncols {
+ Memr[iptr+i-1] = Memr[buf]
+ buf = buf + cstep
+ }
+ iptr = iptr + ncols
+ }
+
+ return (npts)
+end
+
+
+# RG_LMODE -- Compute mode of an array. The mode is found by binning
+# with a bin size based on the data range over a fraction of the
+# pixels about the median and a bin step which may be smaller than the
+# bin size. If there are too few points the median is returned.
+# The input array must be sorted.
+
+real procedure rg_lmode (a, npts, nmin, zrange, fzbin, fzstep)
+
+real a[npts] #I the sorted input data array
+int npts #I the number of points
+int nmin #I the minimum number of points
+real zrange #I fraction of pixels around median to use
+real fzbin #I the bin size for the mode search
+real fzstep #I the step size for the mode search
+
+int x1, x2, x3, nmax
+real zstep, zbin, y1, y2, mode
+bool fp_equalr()
+
+begin
+ # If there are too few points return the median.
+ if (npts < nmin) {
+ if (mod (npts,2) == 1)
+ return (a[1+npts/2])
+ else
+ return ((a[npts/2] + a[1+npts/2]) / 2.0)
+ }
+
+ # Compute the data range that will be used to do the mode search.
+ # If the data has no range then the constant value will be returned.
+ x1 = max (1, int (1.0 + npts * (1.0 - zrange) / 2.0))
+ x3 = min (npts, int (1.0 + npts * (1.0 + zrange) / 2.0))
+ if (fp_equalr (a[x1], a[x3]))
+ return (a[x1])
+
+ # Compute the bin and step size. The bin size is based on the
+ # data range over a fraction of the pixels around the median
+ # and a bin step which may be smaller than the bin size.
+
+ zstep = fzstep * (a[x3] - a[x1])
+ zbin = fzbin * (a[x3] - a[x1])
+
+ nmax = 0
+ x2 = x1
+ for (y1 = a[x1]; x2 < x3; y1 = y1 + zstep) {
+ for (; a[x1] < y1; x1 = x1 + 1)
+ ;
+ y2 = y1 + zbin
+ for (; (x2 < x3) && (a[x2] < y2); x2 = x2 + 1)
+ ;
+ if (x2 - x1 > nmax) {
+ nmax = x2 - x1
+ if (mod (x2+x1,2) == 0)
+ mode = a[(x2+x1)/2]
+ else
+ mode = (a[(x2+x1)/2] + a[(x2+x1)/2+1]) / 2.0
+ }
+ }
+
+ return (mode)
+end
+
+
+# RG_LLSQFIT -- Compute the bscale and bzero factors by doing a least squares
+# fit to the region data. For this technque to be successful the data must
+# be registered and psf matched.
+
+procedure rg_llsqfit (ls, i, bscale, bzero, bserr, bzerr, chi)
+
+pointer ls #I pointer to the intensity scaling structure
+int i #I the current region
+real bscale #O the computed bscale factor
+real bzero #O the computed bzero factor
+real bserr #O the estimated error in bscale
+real bzerr #O the estimated error in bzero
+real chi #O the output chi at unit weight
+
+int j, npts
+pointer rbuf, ibuf, rerr, ierr, weight
+real rgain, igain, rrnoise, irnoise, answers[MAX_NFITPARS]
+real datamin, datamax
+int rg_lstati()
+pointer rg_lstatp()
+real rg_lstatr()
+
+begin
+ # Get the data pointers.
+ rbuf = rg_lstatp (ls, RBUF)
+ ibuf = rg_lstatp (ls, IBUF)
+
+ # Allocate space for the error and weight arrays.
+ npts = Memi[rg_lstatp(ls,RNPTS)+i-1]
+ call malloc (rerr, npts, TY_REAL)
+ call malloc (ierr, npts, TY_REAL)
+ call malloc (weight, npts, TY_REAL)
+
+ # Compute the errors.
+ rgain = rg_lstatr (ls, RGAIN)
+ igain = rg_lstatr (ls, IGAIN)
+ rrnoise = rg_lstatr (ls, RREADNOISE) ** 2 / rgain
+ irnoise = rg_lstatr (ls, IREADNOISE) ** 2 / igain
+ do j = 1, npts {
+ Memr[rerr+j-1] = (Memr[rbuf+j-1] + rrnoise) / rgain
+ Memr[ierr+j-1] = (Memr[ibuf+j-1] + irnoise) / igain
+ }
+
+ # Compute the weights.
+ if (IS_INDEFR(rg_lstatr(ls,DATAMIN)) && IS_INDEFR(ls,DATAMAX))
+ call amovkr (1.0, Memr[weight], npts)
+ else {
+ if (IS_INDEFR(rg_lstatr(ls,DATAMIN)))
+ datamin = -MAX_REAL
+ else
+ datamin = rg_lstatr (ls, DATAMIN)
+ if (IS_INDEFR(rg_lstatr(ls,DATAMAX)))
+ datamax = MAX_REAL
+ else
+ datamax = rg_lstatr (ls, DATAMAX)
+ do j = 1, npts {
+ if (Memr[rbuf+j-1] < datamin || Memr[rbuf+j-1] > datamax)
+ Memr[weight+j-1] = 0.0
+ else if (Memr[ibuf+j-1] < datamin || Memr[ibuf+j-1] > datamax)
+ Memr[weight+j-1] = 0.0
+ else
+ Memr[weight+j-1] = 1.0
+ }
+ }
+
+ # Compute the fit.
+ call ll_lsqf1 (Memr[ibuf], Memr[rbuf], Memr[ierr], Memr[rerr],
+ Memr[weight], npts, rg_lstati(ls, MAXITER), answers)
+
+ # Perform the rejection cycle.
+ if (npts > 2 && rg_lstati(ls,NREJECT) > 0 &&
+ (! IS_INDEFR(rg_lstatr(ls,LOREJECT)) ||
+ ! IS_INDEFR(rg_lstatr(ls,HIREJECT))))
+ call ll_rlsqf1 (Memr[ibuf], Memr[rbuf], Memr[ierr], Memr[rerr],
+ Memr[weight], npts, rg_lstati(ls,MAXITER), answers,
+ rg_lstati(ls,NREJECT), rg_lstatr(ls,LOREJECT),
+ rg_lstatr(ls,HIREJECT))
+ bscale = SLOPE[answers]
+ bzero = YINCPT[answers]
+ bserr = ESLOPE[answers]
+ bzerr = EYINCPT[answers]
+ chi = CHI[answers]
+
+ # Free the working space.
+ call mfree (rerr, TY_REAL)
+ call mfree (ierr, TY_REAL)
+ call mfree (weight, TY_REAL)
+end
+
+
+# RG_RAVSTATS -- Compute the average statistics.
+
+procedure rg_ravstats (ls, sumbscale, sumbzero, sumwbscale, sumwbzero, sumbserr,
+ sumbzerr, bserr, bzerr, avbscale, avbzero, avbserr, avbzerr, ngood)
+
+pointer ls #I pointer to the linmatch structure
+double sumbscale #I/O sum of the bscale values
+double sumbzero #I/O sum of the bzero values
+double sumwbscale #I/O sum of the weighted bscale values
+double sumwbzero #I/O sum of the weighted bzero values
+double sumbserr #I/O sum of the bscale error
+double sumbzerr #I/O sum of the bscale error
+real bserr #I/O the bscale error of 1 observation
+real bzerr #I/O the bzero error of 1 observation
+real avbscale #I/O the average bscale factor
+real avbzero #I/O the average bzero factor
+real avbserr #O the average bscale error factor
+real avbzerr #O the average bzero error factor
+int ngood #I/O the number of good data values
+
+int i, nregions, nrej, nbad
+real sigbscale, sigbzero, lobscale, hibscale, lobzero, hibzero
+real bscale, bzero, bsresid, bzresid
+double dw
+int rg_lstati()
+pointer rg_lstatp()
+real rg_lsigma(), rg_lstatr()
+
+begin
+ nregions = rg_lstati (ls,NREGIONS)
+
+ nrej = 0
+ repeat {
+
+ # Compute sigma.
+ sigbscale = rg_lsigma (Memr[rg_lstatp(ls,RBSCALE)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, avbscale)
+ if (sigbscale <= 0.0)
+ break
+ sigbzero = rg_lsigma (Memr[rg_lstatp(ls,RBZERO)],
+ Memi[rg_lstatp(ls,RDELETE)], nregions, avbzero)
+ if (sigbzero <= 0.0)
+ break
+
+ if (IS_INDEFR(rg_lstatr(ls,LOREJECT))) {
+ lobscale = -MAX_REAL
+ lobzero = -MAX_REAL
+ } else {
+ lobscale = -sigbscale * rg_lstatr (ls, LOREJECT)
+ lobzero = -sigbzero * rg_lstatr (ls, LOREJECT)
+ }
+ if (IS_INDEFR(rg_lstatr(ls,HIREJECT))) {
+ hibscale = MAX_REAL
+ hibzero = MAX_REAL
+ } else {
+ hibscale = sigbscale * rg_lstatr (ls, HIREJECT)
+ hibzero = sigbzero * rg_lstatr (ls, HIREJECT)
+ }
+
+ nbad = 0
+ do i = 1, nregions {
+ if (Memi[rg_lstatp(ls,RDELETE)+i-1] != LS_NO)
+ next
+ bscale = Memr[rg_lstatp(ls,RBSCALE)+i-1]
+ if (IS_INDEFR(bscale))
+ next
+ bzero = Memr[rg_lstatp(ls,RBZERO)+i-1]
+ if (IS_INDEFR(bzero))
+ next
+ bserr = Memr[rg_lstatp(ls,RBSCALEERR)+i-1]
+ bsresid = bscale - avbscale
+ bzerr = Memr[rg_lstatp(ls,RBZEROERR)+i-1]
+ bzresid = bzero - avbzero
+ if (bsresid >= lobscale && bsresid <= hibscale && bzresid >=
+ lobzero && bzresid <= hibzero)
+ next
+
+ if (bserr <= 0.0)
+ dw = 1.0d0
+ else
+ dw = 1.0d0 / bserr ** 2
+ sumbscale = sumbscale - dw * bscale
+ sumbserr = sumbserr - dw * bscale * bscale
+ sumwbscale = sumwbscale - dw
+
+ if (bzerr <= 0.0)
+ dw = 1.0d0
+ else
+ dw = 1.0d0 / bzerr ** 2
+ sumbzero = sumbzero - dw * bzero
+ sumbzerr = sumbzerr - dw * bzero * bzero
+ sumwbzero = sumwbzero - dw
+
+ nbad = nbad + 1
+ Memi[rg_lstatp(ls,RDELETE)+i-1] = LS_BADSIGMA
+ ngood = ngood - 1
+ }
+
+ if (nbad <= 0)
+ break
+
+ call rg_avstats (sumbscale, sumbzero, sumwbscale, sumwbzero,
+ sumbserr, sumbzerr, bserr, bzerr, avbscale, avbzero,
+ avbserr, avbzerr, ngood)
+ if (ngood <= 0)
+ break
+
+ nrej = nrej + 1
+
+ } until (nrej >= rg_lstati(ls,NREJECT))
+end
+
+
+# RG_AVSTATS -- Compute the average statistics.
+
+procedure rg_avstats (sumbscale, sumbzero, sumwbscale, sumwbzero, sumbserr,
+ sumbzerr, bserr, bzerr, avbscale, avbzero, avbserr, avbzerr, ngood)
+
+double sumbscale #I sum of the bscale values
+double sumbzero #I sum of the bzero values
+double sumwbscale #I sum of the weighted bscale values
+double sumwbzero #I sum of the weighted bzero values
+double sumbserr #I sum of the bscale error
+double sumbzerr #I sum of the bscale error
+real bserr #I the bscale error of 1 observation
+real bzerr #I the bzero error of 1 observation
+real avbscale #O the average bscale factor
+real avbzero #O the average bzero factor
+real avbserr #O the average bscale error factor
+real avbzerr #O the average bzero error factor
+int ngood #I the number of good data values
+
+begin
+ # Compute the average scaling factors.
+ if (ngood > 0) {
+ avbscale = sumbscale / sumwbscale
+ if (ngood > 1) {
+ avbserr = ngood * (sumbserr / sumwbscale - (sumbscale /
+ sumwbscale) ** 2) /
+ (ngood - 1)
+ if (avbserr >= 0.0)
+ avbserr = sqrt (avbserr)
+ else
+ avbserr = 0.0
+ } else
+ avbserr = bserr
+ avbzero = sumbzero / sumwbzero
+ if (ngood > 1) {
+ avbzerr = ngood * (sumbzerr / sumwbzero - (sumbzero /
+ sumwbzero) ** 2) /
+ (ngood - 1)
+ if (avbzerr >= 0.0)
+ avbzerr = sqrt (avbzerr)
+ else
+ avbzerr = 0.0
+ } else
+ avbzerr = bzerr
+ } else {
+ avbscale = 1.0
+ avbzero = 0.0
+ avbserr = INDEFR
+ avbzerr = INDEFR
+ }
+end
+
+
+# RG_LSIGMA -- Compute the standard deviation of an array taken into
+# account any existing deletions.
+
+real procedure rg_lsigma (a, del, npts, mean)
+
+real a[ARB] #I the input array
+int del[ARB] #I the deletions array
+int npts #I the number of points in the array
+real mean #I the mean of the array
+
+int i, ngood
+double sumsq
+
+begin
+ sumsq = 0.0d0
+ ngood = 0
+
+ do i = 1, npts {
+ if (del[i] != LS_NO)
+ next
+ if (IS_INDEFR(a[i]))
+ next
+ sumsq = sumsq + (a[i] - mean) ** 2
+ ngood = ngood + 1
+ }
+
+ if (ngood <= 1)
+ return (0.0)
+ else if (sumsq <= 0.0)
+ return (0.0)
+ else
+ return (sqrt (real (sumsq / (ngood - 1))))
+end
diff --git a/pkg/images/immatch/src/linmatch/rglshow.x b/pkg/images/immatch/src/linmatch/rglshow.x
new file mode 100644
index 00000000..1bf2c65f
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rglshow.x
@@ -0,0 +1,107 @@
+include "linmatch.h"
+
+# RG_LSHOW -- Print the LINMATCH task parameters.
+
+procedure rg_lshow (ls)
+
+pointer ls #I pointer to linmatch structure
+
+pointer sp, str1, str2
+int rg_lstati()
+real rg_lstatr()
+
+begin
+ call smark (sp)
+ call salloc (str1, SZ_LINE, TY_CHAR)
+ call salloc (str2, SZ_LINE, TY_CHAR)
+
+ call printf ("\nIntensity Matching Parameters\n")
+ if (rg_lstati (ls, BSALGORITHM) != LS_PHOTOMETRY && rg_lstati(ls,
+ BZALGORITHM) != LS_PHOTOMETRY) {
+ call rg_lstats (ls, IMAGE, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str1])
+ call rg_lstats (ls, REFIMAGE, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str1])
+ call rg_lstats (ls, REGIONS, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_REGIONS)
+ call pargstr (Memc[str1])
+ call rg_lstats (ls, CCDGAIN, Memc[str1], SZ_LINE)
+ call rg_lstats (ls, CCDREAD, Memc[str2], SZ_LINE)
+ call printf (" %s: %s %s: %s\n")
+ call pargstr (KY_GAIN)
+ call pargstr (Memc[str1])
+ call pargstr (KY_READNOISE)
+ call pargstr (Memc[str2])
+ } else {
+ call rg_lstats (ls, IMAGE, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str1])
+ call rg_lstats (ls, PHOTFILE, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str1])
+ call rg_lstats (ls, REFIMAGE, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str1])
+ }
+ call rg_lstats (ls, SHIFTSFILE, Memc[str1], SZ_FNAME)
+ if (Memc[str1] != EOS) {
+ call printf (" %s: %s\n")
+ call pargstr (KY_SHIFTSFILE)
+ call pargstr (Memc[str1])
+ } else {
+ call printf (" %s: %g %s: %g\n")
+ call pargstr (KY_XSHIFT)
+ call pargr (rg_lstatr(ls,XSHIFT))
+ call pargstr (KY_YSHIFT)
+ call pargr (rg_lstatr(ls,YSHIFT))
+ }
+ call printf (" %s: %d %s: %d\n")
+ call pargstr (KY_DNX)
+ call pargi (rg_lstati(ls,DNX))
+ call pargstr (KY_DNY)
+ call pargi (rg_lstati(ls,DNY))
+
+ call rg_lstats (ls, DATABASE, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s")
+ call pargstr (KY_DATABASE)
+ call pargstr (Memc[str1])
+ call rg_lstats (ls, OUTIMAGE, Memc[str1], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_OUTIMAGE)
+ call pargstr (Memc[str1])
+
+ call rg_lstats (ls, BSSTRING, Memc[str1], SZ_LINE)
+ call rg_lstats (ls, BZSTRING, Memc[str2], SZ_LINE)
+ call printf (" %s: %s %s\n")
+ call pargstr ("scaling")
+ call pargstr (Memc[str1])
+ call pargstr (Memc[str2])
+ call printf (" %s = %g %s = %g")
+ call pargstr (KY_DATAMIN)
+ call pargr (rg_lstatr (ls, DATAMIN))
+ call pargstr (KY_DATAMAX)
+ call pargr (rg_lstatr (ls, DATAMAX))
+ call printf (" %s: %d\n")
+ call pargstr (KY_MAXITER)
+ call pargi (rg_lstati(ls,MAXITER))
+ call printf (" %s: %d")
+ call pargstr (KY_NREJECT)
+ call pargi (rg_lstati(ls,NREJECT))
+ call printf (" %s = %g %s = %g\n")
+ call pargstr (KY_LOREJECT)
+ call pargr (rg_lstatr (ls, LOREJECT))
+ call pargstr (KY_HIREJECT)
+ call pargr (rg_lstatr (ls, HIREJECT))
+
+ call printf ("\n")
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/linmatch/rglsqfit.x b/pkg/images/immatch/src/linmatch/rglsqfit.x
new file mode 100644
index 00000000..f728ecde
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rglsqfit.x
@@ -0,0 +1,443 @@
+include <mach.h>
+include "lsqfit.h"
+
+# LL_RLSQF1 -- Given an initial fit reject points outside of the low and
+# high cut rejections parameters.
+
+procedure ll_rlsqf1 (x, y, xerr, yerr, weight, npts, maxiter, answers, nreject,
+ locut, hicut)
+
+real x[ARB] #I the input vector
+real y[ARB] #I the reference vector
+real xerr[ARB] #I the input vector errors squared
+real yerr[ARB] #I the reference vector errors squared
+real weight[ARB] #I the input weight array
+int npts #I the number of points
+int maxiter #I the number of iterations
+real answers[ARB] #I/O the answers array
+int nreject #I the max number of rejection cycles
+real locut #I the low side rejection parameter
+real hicut #I the high side rejection parameter
+
+int i, niter, nrej
+real loval, hival, resid
+
+begin
+ if ((IS_INDEFR(locut) && IS_INDEFR(hicut)) || npts <= 2)
+ return
+ if (RMS[answers] <= 0.0 || IS_INDEFR(CHI[answers]))
+ return
+
+ niter = 0
+ repeat {
+ if (IS_INDEFR(locut))
+ loval = -MAX_REAL
+ else
+ loval = -locut * RMS[answers]
+ if (IS_INDEFR(hicut))
+ hival = MAX_REAL
+ else
+ hival = hicut * RMS[answers]
+ nrej = 0
+ do i = 1, npts {
+ if (weight[i] <= 0.0)
+ next
+ resid = y[i] - (SLOPE[answers] * x[i] + YINCPT[answers])
+ if (resid >= loval && resid <= hival)
+ next
+ weight[i] = 0.0
+ nrej = nrej + 1
+ }
+ if (nrej <= 0)
+ break
+ call ll_lsqf1 (x, y, xerr, yerr, weight, npts, maxiter, answers)
+ if (IS_INDEFR(CHI[answers]))
+ break
+ if (RMS[answers] <= 0.0)
+ break
+ niter = niter + 1
+ } until (niter >= nreject)
+end
+
+
+# LL_LSQF1 -- Compute the slope and intercept of the equation y = a * x + b
+# using error arrays in both x and y.
+
+procedure ll_lsqf1 (x, y, xerr, yerr, weight, npts, niter, answers)
+
+real x[ARB] #I the input vector
+real y[ARB] #I the reference vector
+real xerr[ARB] #I the input vector errors squared
+real yerr[ARB] #I the reference vector errors squared
+real weight[ARB] #I the input weight array
+int npts #I the number of points
+int niter #I the number of iterations
+real answers[ARB] #I/O the answers array
+
+int i, j
+pointer bufr, bufx, bufw
+real slope, yintrcpt, me1, msq, wt, dm, db
+
+begin
+ # Peform the initial fit.
+ call ll_0lsqf1 (x, y, weight, npts, answers)
+ if (IS_INDEFR(CHI[answers]))
+ return
+
+ # Allocate working space.
+ call malloc (bufr, npts, TY_REAL)
+ call malloc (bufx, npts, TY_REAL)
+ call malloc (bufw, npts, TY_REAL)
+
+ # Initialize the iterations.
+ slope = SLOPE[answers]
+ yintrcpt = YINCPT[answers]
+ me1 = CHI[answers]
+
+ # Iterate on the fit.
+ do i = 1, niter {
+ msq = slope * slope
+ do j = 1, npts {
+ if (weight[j] <= 0.0) {
+ Memr[bufr+j-1] = 0.0
+ Memr[bufw+j-1] = 0.0
+ Memr[bufx+j-1] = 0.0
+ } else {
+ wt = yerr[j] + msq * xerr[j]
+ if (wt <= 0.0)
+ wt = 1.0
+ else
+ wt = 1.0 / wt
+ Memr[bufr+j-1] = y[j] - (slope * x[j] + yintrcpt)
+ Memr[bufw+j-1] = weight[j] * wt
+ Memr[bufx+j-1] = x[j] + Memr[bufr+j-1] * slope * xerr[j] *
+ wt
+ }
+ }
+ call ll_0lsqf1 (Memr[bufx], Memr[bufr], Memr[bufw], npts, answers)
+ if (IS_INDEFR(CHI[answers]))
+ break
+ if (abs ((me1 - CHI[answers]) / CHI[answers]) < 1.0e-5)
+ break
+ dm = SLOPE[answers]
+ db = YINCPT[answers]
+ me1 = CHI[answers]
+ slope = slope + dm
+ yintrcpt = yintrcpt + db
+ }
+
+ # Compute the final answers.
+ SLOPE[answers] = slope
+ YINCPT[answers] = yintrcpt
+
+ call mfree (bufr, TY_REAL)
+ call mfree (bufx, TY_REAL)
+ call mfree (bufw, TY_REAL)
+end
+
+
+# LL_0LSQF1: Compute the slope and intercept of the equation y = a * x + b
+# using errors in y only.
+
+procedure ll_0lsqf1 (x, y, w, npts, answers)
+
+real x[ARB] #I the input vector
+real y[ARB] #I the reference vector
+real w[ARB] #I the weight vector
+int npts #I the number of points
+real answers[ARB] #I the answers
+
+int i, ngood
+double sumyy, sumxx, sumxy, sumx, sumy, sumw
+double a, b, det
+real wressq, ressq
+bool fp_equald()
+double ll_dsum1(), ll_dsum2(), ll_dsum3()
+
+begin
+ # Compute the determinant.
+ sumyy = ll_dsum3 (y, y, w, npts)
+ sumxx = ll_dsum3 (x, x, w, npts)
+ sumxy = ll_dsum3 (x, y, w, npts)
+ sumy = ll_dsum2 (y, w, npts)
+ sumx = ll_dsum2 (x, w, npts)
+ sumw = ll_dsum1 (w, npts)
+ det = sumw * sumxx - sumx * sumx
+
+ if (fp_equald (0.0d0, det)) {
+ SLOPE[answers] = INDEFR
+ YINCPT[answers] = INDEFR
+ ESLOPE[answers] = INDEFR
+ EYINCPT[answers] = INDEFR
+ CHI[answers] = INDEFR
+ RMS[answers] = INDEFR
+ } else {
+ a = (sumw * sumxy - sumx * sumy) / det
+ b = (sumxx * sumy - sumx * sumxy) / det
+ ngood = 0.0
+ ressq = 0.0
+ do i = 1, npts {
+ if (w[i] > 0.0) {
+ ngood = ngood + 1
+ ressq = ressq + (y[i] - (a * x[i] + b)) ** 2
+ }
+ }
+ SLOPE[answers] = a
+ YINCPT[answers] = b
+ wressq = sumyy + a * (a * sumxx + 2. * (b * sumx - sumxy)) +
+ b * (b * sumw - 2.0 * sumy)
+ if (ngood <= 2) {
+ CHI[answers] = 0.0
+ ESLOPE[answers] = 0.0
+ EYINCPT[answers] = 0.0
+ RMS[answers] = 0.0
+ } else if (wressq >= 0.0) {
+ CHI[answers] = sqrt (wressq / (ngood - 2))
+ ESLOPE[answers] = CHI[answers] * sqrt (real (sumw / abs(det)))
+ EYINCPT[answers] = CHI[answers] * sqrt (real (sumxx / abs(det)))
+ RMS[answers] = sqrt (ressq / (ngood - 2))
+ } else {
+ CHI[answers] = 0.0
+ ESLOPE[answers] = 0.0
+ EYINCPT[answers] = 0.0
+ RMS[answers] = 0.0
+ }
+ }
+end
+
+
+## GET_LSQF2: iterate LSq Fit to z=ax+by+c for errors in x, y and z.
+## NB: xerr, yerr, zerr are errors SQUARED.
+##
+#
+#procedure get_lsqf2 (x, y, z, xerr, yerr, zerr, weight, npts, niter, stats)
+#
+#real x[npts], y[npts], z[npts] # data vectors
+#real xerr[npts], yerr[npts], zerr[npts] # error ** 2 vectors
+#real weight[npts] # additional weight factors
+#int npts # vector lengths
+#int niter # no. of iterations
+#real stats[NFITPAR] # returned fit params
+#
+#int i, j
+#real a, b, c, me1
+#pointer bufr, bufx, bufy, bufw
+#real asq, bsq, res, wt, da, db, dc
+#
+#begin
+# call malloc (bufr, npts, TY_REAL)
+# call malloc (bufx, npts, TY_REAL)
+# call malloc (bufy, npts, TY_REAL)
+# call malloc (bufw, npts, TY_REAL)
+#
+## initial fit; NB needs expansion
+# call get_0lsqf2 (x, y, z, weight, npts, stats)
+# a = SLOPE1[stats]
+# b = SLOPE2[stats]
+# c = OFFSET[stats]
+# me1 = CHI[stats]
+## call printf ("iteration: %2d a=%7.4f b=%7.4f off=%6.2f (%7.3f) \n")
+## call pargi (0)
+## call pargr (a)
+## call pargr (b)
+## call pargr (c)
+## call pargr (me1)
+#
+## iterate
+# do i = 1, niter {
+# asq = a * a
+# bsq = b * b
+# do j = 1, npts {
+# res = z[j] - (a * x[j] + b * y[j] + c)
+# wt = 1. / (zerr[j] + asq * xerr[j] + bsq * yerr[j])
+# Memr[bufr+j-1] = res
+# Memr[bufw+j-1] = weight[j] * wt
+# Memr[bufx+j-1] = x[j] + res * a * xerr[j] * wt
+# Memr[bufy+j-1] = y[j] + res * b * yerr[j] * wt
+# }
+# call get_0lsqf2 (Memr[bufx], Memr[bufy], Memr[bufr], Memr[bufw], npts, stats)
+# da = SLOPE1[stats]
+# db = SLOPE2[stats]
+# dc = OFFSET[stats]
+# me1 = CHI[stats]
+# a = a + da
+# b = b + db
+# c = c + dc
+## call printf ("iteration: %2d a=%7.4f b=%7.4f off=%6.2f (%7.3f) \n")
+## call pargi (i)
+## call pargr (a)
+## call pargr (b)
+## call pargr (c)
+## call pargr (me1)
+# }
+#
+# SLOPE1[stats] = a
+# SLOPE2[stats] = b
+# OFFSET[stats] = c
+#
+# call mfree (bufr, TY_REAL)
+# call mfree (bufx, TY_REAL)
+# call mfree (bufy, TY_REAL)
+# call mfree (bufw, TY_REAL)
+#end
+#
+##
+## GET_0LSQF2 -- calculate the zeroth order LLSq Fit for 2 independent variables,
+## assumming errors in z only
+##
+#
+# procedure get_0lsqf2 (x, y, z, w, npt, stats)
+#
+#real x[npt], y[npt] # input coords
+#real z[npt] # ref. coord.
+#real w[npt] # weights
+#int npt # number of points
+#real stats[NFITPAR] # fit info struct
+#
+#real ga[4, 3]
+#
+#double dsum1(), dsum2(), dsum3()
+#
+#begin
+# ga[1,1] = dsum3 (x, x, w, npt)
+# ga[2,1] = dsum3 (x, y, w, npt)
+# ga[2,2] = dsum3 (y, y, w, npt)
+# ga[3,1] = dsum2 (x, w, npt)
+# ga[3,2] = dsum2 (y, w, npt)
+# ga[4,1] = dsum3 (x, z, w, npt)
+# ga[4,2] = dsum3 (y, z, w, npt)
+# ga[4,3] = dsum2 (z, w, npt)
+# ga[3,3] = dsum1 (w, npt)
+#
+# ga[1,2] = ga[2,1]
+# ga[1,3] = ga[3,1]
+# ga[2,3] = ga[3,2]
+#
+# call g_elim(ga, 3)
+#
+# SLOPE1[stats] = ga[4,1]
+# SLOPE2[stats] = ga[4,2]
+# OFFSET[stats] = ga[4,3]
+##need to define errors, me1
+# EOFFSET[stats] = INDEF
+# ESLOPE1[stats] = INDEF
+# ESLOPE2[stats] = INDEF
+# CHI[stats] = INDEF
+#end
+#
+
+
+# LL_LLSQF0 -- Compute the offset b in the equation y - x = b using error
+# arrays in both x and y.
+
+#procedure ll_lsqf0 (x, y, xerr, yerr, w, npts, answers)
+
+#real x[ARB] #I the input vector
+#real y[ARB] #I the reference vector
+#real xerr[ARB] #I the input vector errors squared
+#real yerr[ARB] #I the reference vector errors squared
+#real w[ARB] #I the input weight vector
+#int npts #I the number of points
+#real answers[ARB] #I the answer vector
+
+#double sumxx, sumx, sumw
+#pointer bufr, bufw
+#double ll_dsum1(), ll_dsum2(), ll_dsum3()
+
+#begin
+# # Allocate working space.
+# call malloc (bufr, npts, TY_REAL)
+# call malloc (bufw, npts, TY_REAL)
+#
+# call asubr (y, x, Memr[bufr], npts)
+# call aaddr (yerr, xerr, Memr[bufw], npts)
+# call adivr (w, Memr[bufw], Memr[bufw], npts)
+#
+# sumxx = ll_dsum3 (Memr[bufr], Memr[bufr], Memr[bufw], npts)
+# sumx = ll_dsum2 (Memr[bufr], Memr[bufw], npts)
+# sumw = ll_dsum1 (Memr[bufw], npts)
+#
+# if (sumw <= 0.0d0) {
+# OFFSET[answers] = INDEFR
+# EOFFSET[answers] = INDEFR
+# CHI[answers] = INDEFR
+# } else {
+# OFFSET[answers] = sumx / sumw
+# if (npts > 1) {
+# CHI[answers] = sqrt (real ((sumxx - sumx * sumx / sumw) /
+# (npts - 1)))
+# EOFFSET[answers] = CHI[answers] / sqrt (real (sumw))
+# } else {
+# CHI[answers] = 0.0
+# EOFFSET[answers] = 0.0
+# }
+# }
+#
+# # Free working space.
+# call mfree (bufr, TY_REAL)
+# call mfree (bufw, TY_REAL)
+#end
+
+
+# LL_DSUM1 -- Compute a double precision vector sum.
+
+double procedure ll_dsum1 (a, n)
+
+real a[ARB] #I the input vector
+int n #I the number of points
+
+double sum
+int i
+
+begin
+ sum = 0.0d0
+ do i = 1, n
+ sum = sum + a[i]
+
+ return (sum)
+end
+
+
+# LL_DSUM2 -- Compute a double precision vector product.
+
+double procedure ll_dsum2 (a, b, n)
+
+real a[n] #I the input vector
+real b[n] #I the weight vector
+int n #I the number of points
+
+double sum
+int i
+
+begin
+ sum = 0.0d0
+ do i = 1, n {
+ if (b[i] > 0.0)
+ sum = sum + a[i] * b[i]
+ }
+
+ return (sum)
+end
+
+
+# LL_DSUM3 -- Compute a double precision weighted dot product.
+
+
+double procedure ll_dsum3 (a, b, c, n)
+
+real a[n] #I first input vector
+real b[n] #I second input vector
+real c[n] #I input weight vector
+int n #I the number of points
+
+double sum
+int i
+
+begin
+ sum = 0.0d0
+ do i = 1, n
+ if (c[i] > 0.0)
+ sum = sum + a[i] * b[i] * c[i]
+
+ return (sum)
+end
diff --git a/pkg/images/immatch/src/linmatch/rgltools.x b/pkg/images/immatch/src/linmatch/rgltools.x
new file mode 100644
index 00000000..845a0ac4
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/rgltools.x
@@ -0,0 +1,1017 @@
+include "linmatch.h"
+
+# RG_LINIT -- Initialize the linscale structure.
+
+procedure rg_linit (ls, max_nregions)
+
+pointer ls #I/O pointer to the intensity scaling structure
+int max_nregions #I the maximum number of regions
+
+begin
+ # Allocate the temporary space.
+ call malloc (ls, LEN_LSSTRUCT, TY_STRUCT)
+
+ # Set up the regions parameters.
+ LS_NREGIONS(ls) = 0
+ LS_CNREGION(ls) = 1
+ LS_MAXNREGIONS(ls) = max_nregions
+
+ # Initialize the pointers.
+ LS_RC1(ls) = NULL
+ LS_RC2(ls) = NULL
+ LS_RL1(ls) = NULL
+ LS_RL2(ls) = NULL
+ LS_RXSTEP(ls) = NULL
+ LS_RYSTEP(ls) = NULL
+ LS_XSHIFT(ls) = 0.0
+ LS_YSHIFT(ls) = 0.0
+ LS_SXSHIFT(ls) = 0.0
+ LS_SYSHIFT(ls) = 0.0
+
+ LS_RBUF(ls) = NULL
+ LS_RGAIN(ls) = 1.0
+ LS_RREADNOISE(ls) = 0.0
+ LS_RMEAN(ls) = NULL
+ LS_RMEDIAN(ls) = NULL
+ LS_RMODE(ls) = NULL
+ LS_RSIGMA(ls) = NULL
+ LS_RSKY(ls) = NULL
+ LS_RSKYERR(ls) = NULL
+ LS_RMAG(ls) = NULL
+ LS_RMAGERR(ls) = NULL
+ LS_RNPTS(ls) = NULL
+
+ LS_IBUF(ls) = NULL
+ LS_IGAIN(ls) = 1.0
+ LS_IREADNOISE(ls) = 0.0
+ LS_IMEAN(ls) = NULL
+ LS_IMEDIAN(ls) = NULL
+ LS_IMODE(ls) = NULL
+ LS_ISIGMA(ls) = NULL
+ LS_ISKY(ls) = NULL
+ LS_ISKYERR(ls) = NULL
+ LS_IMAG(ls) = NULL
+ LS_IMAGERR(ls) = NULL
+ LS_INPTS(ls) = NULL
+
+ LS_RBSCALE(ls) = NULL
+ LS_RBSCALEERR(ls) = NULL
+ LS_RBZERO(ls) = NULL
+ LS_RBZEROERR(ls) = NULL
+ LS_RDELETE(ls) = NULL
+ LS_RCHI(ls) = NULL
+
+ # Initialize the scaling algorithm parameters.
+ LS_BZALGORITHM(ls) = DEF_BZALGORITHM
+ LS_BSALGORITHM(ls) = DEF_BSALGORITHM
+ LS_CBZERO(ls) = DEF_CBZERO
+ LS_CBSCALE(ls) = DEF_CBSCALE
+ LS_DNX(ls) = DEF_DNX
+ LS_DNY(ls) = DEF_DNY
+ LS_MAXITER(ls) = DEF_MAXITER
+ LS_DATAMIN(ls) = DEF_DATAMIN
+ LS_DATAMAX(ls) = DEF_DATAMAX
+ LS_NREJECT(ls) = DEF_NREJECT
+ LS_LOREJECT(ls) = DEF_LOREJECT
+ LS_HIREJECT(ls) = DEF_HIREJECT
+ LS_GAIN(ls) = DEF_GAIN
+ LS_READNOISE(ls) = DEF_READNOISE
+
+ # Initialize the answers
+ LS_TBZERO(ls) = 0.0
+ LS_TBZEROERR(ls) = INDEFR
+ LS_TBSCALE(ls) = 1.0
+ LS_TBSCALEERR(ls) = INDEFR
+
+ # Initialize the strings.
+ call strcpy ("mean", LS_BSSTRING(ls), SZ_FNAME)
+ call strcpy ("mean", LS_BZSTRING(ls), SZ_FNAME)
+ LS_CCDGAIN(ls) = EOS
+ LS_CCDREAD(ls) = EOS
+ LS_IMAGE(ls) = EOS
+ LS_REFIMAGE(ls) = EOS
+ LS_REGIONS(ls) = EOS
+ LS_DATABASE(ls) = EOS
+ LS_OUTIMAGE(ls) = EOS
+ LS_RECORD(ls) = EOS
+ LS_SHIFTSFILE(ls) = EOS
+ LS_PHOTFILE(ls) = EOS
+
+ # Initialize the buffers.
+ call rg_lrinit (ls)
+end
+
+
+# RG_LRINIT -- Initialize the region dependent part of the linscale structure.
+
+procedure rg_lrinit (ls)
+
+pointer ls #I pointer to the intensity scaling structure
+
+begin
+ # Free up previously defined region pointers.
+ call rg_lrfree (ls)
+
+ # Allocate region definition pointers.
+ call malloc (LS_RC1(ls), LS_MAXNREGIONS(ls), TY_INT)
+ call malloc (LS_RC2(ls), LS_MAXNREGIONS(ls), TY_INT)
+ call malloc (LS_RL1(ls), LS_MAXNREGIONS(ls), TY_INT)
+ call malloc (LS_RL2(ls), LS_MAXNREGIONS(ls), TY_INT)
+ call malloc (LS_RXSTEP(ls), LS_MAXNREGIONS(ls), TY_INT)
+ call malloc (LS_RYSTEP(ls), LS_MAXNREGIONS(ls), TY_INT)
+
+ # Allocate region statistics pointers.
+ call malloc (LS_RMEAN(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RMEDIAN(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RMODE(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RSIGMA(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RSKY(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RSKYERR(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RMAG(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RMAGERR(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RNPTS(ls), LS_MAXNREGIONS(ls), TY_INT)
+
+ call malloc (LS_IMEAN(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_IMEDIAN(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_IMODE(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_ISIGMA(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_ISKY(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_ISKYERR(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_IMAG(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_IMAGERR(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_INPTS(ls), LS_MAXNREGIONS(ls), TY_INT)
+
+ call malloc (LS_RBSCALE(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RBSCALEERR(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RBZERO(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RBZEROERR(ls), LS_MAXNREGIONS(ls), TY_REAL)
+ call malloc (LS_RDELETE(ls), LS_MAXNREGIONS(ls), TY_INT)
+ call malloc (LS_RCHI(ls), LS_MAXNREGIONS(ls), TY_REAL)
+
+ # Initialize region definitions.
+ call amovki (INDEFI, Memi[LS_RC1(ls)], LS_MAXNREGIONS(ls))
+ call amovki (INDEFI, Memi[LS_RC2(ls)], LS_MAXNREGIONS(ls))
+ call amovki (INDEFI, Memi[LS_RL1(ls)], LS_MAXNREGIONS(ls))
+ call amovki (INDEFI, Memi[LS_RL2(ls)], LS_MAXNREGIONS(ls))
+ call amovki (INDEFI, Memi[LS_RXSTEP(ls)], LS_MAXNREGIONS(ls))
+ call amovki (INDEFI, Memi[LS_RYSTEP(ls)], LS_MAXNREGIONS(ls))
+
+ # Initilaize the statistics.
+ call amovkr (INDEFR, Memr[LS_RMEAN(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RMEDIAN(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RMODE(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RSIGMA(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RSKY(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RSKYERR(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RMAG(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RMAGERR(ls)], LS_MAXNREGIONS(ls))
+ call amovki (INDEFI, Memi[LS_RNPTS(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_IMEAN(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_IMEDIAN(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_IMODE(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_ISIGMA(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_ISKY(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_ISKYERR(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_IMAG(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_IMAGERR(ls)], LS_MAXNREGIONS(ls))
+ call amovki (INDEFI, Memi[LS_INPTS(ls)], LS_MAXNREGIONS(ls))
+
+ # Initialize the answers.
+ call amovkr (INDEFR, Memr[LS_RBSCALE(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RBSCALEERR(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RBZERO(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RBZEROERR(ls)], LS_MAXNREGIONS(ls))
+ call amovki (LS_NO, Memi[LS_RDELETE(ls)], LS_MAXNREGIONS(ls))
+ call amovkr (INDEFR, Memr[LS_RCHI(ls)], LS_MAXNREGIONS(ls))
+end
+
+
+# RG_LINDEFR -- Re-initialize the regions dependent buffers.
+
+procedure rg_lindefr (ls)
+
+pointer ls #I pointer to the intensity scaling structure
+
+int nregions
+int rg_lstati()
+
+begin
+ nregions = rg_lstati (ls, NREGIONS)
+ if (nregions > 0) {
+
+ # Reinitialize the region definition pointers.
+ call amovki (INDEFI, Memi[LS_RC1(ls)], nregions)
+ call amovki (INDEFI, Memi[LS_RC2(ls)], nregions)
+ call amovki (INDEFI, Memi[LS_RL1(ls)], nregions)
+ call amovki (INDEFI, Memi[LS_RL2(ls)], nregions)
+ call amovki (INDEFI, Memi[LS_RXSTEP(ls)], nregions)
+ call amovki (INDEFI, Memi[LS_RYSTEP(ls)], nregions)
+
+ # Reinitialize the statistics pointers.
+ call amovkr (INDEFR, Memr[LS_RMEAN(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RMEDIAN(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RMODE(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RSIGMA(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RSKY(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RSKYERR(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RMAG(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RMAGERR(ls)], nregions)
+ call amovki (INDEFI, Memi[LS_RNPTS(ls)], nregions)
+
+ call amovkr (INDEFR, Memr[LS_IMEAN(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_IMEDIAN(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_IMODE(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_ISIGMA(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_ISKY(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_ISKYERR(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_IMAG(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_IMAGERR(ls)], nregions)
+ call amovki (INDEFI, Memi[LS_INPTS(ls)], nregions)
+
+ # Reinitialize the answers pointers.
+ call amovkr (INDEFR, Memr[LS_RBSCALE(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RBSCALEERR(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RBZERO(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RBZEROERR(ls)], nregions)
+ call amovki (LS_NO, Memi[LS_RDELETE(ls)], nregions)
+ call amovkr (INDEFR, Memr[LS_RCHI(ls)], nregions)
+
+ }
+end
+
+
+# RG_LREALLOC -- Reallocate the regions dependent buffers.
+
+procedure rg_lrealloc (ls, nregions)
+
+pointer ls #I pointer to the intensity scaling structure
+int nregions #I the number of regions
+
+int nr
+int rg_lstati()
+
+begin
+ nr = rg_lstati (ls, NREGIONS)
+
+ # Resize the region definition buffers.
+ call realloc (LS_RC1(ls), nregions, TY_INT)
+ call realloc (LS_RC2(ls), nregions, TY_INT)
+ call realloc (LS_RL1(ls), nregions, TY_INT)
+ call realloc (LS_RL2(ls), nregions, TY_INT)
+ call realloc (LS_RXSTEP(ls), nregions, TY_INT)
+ call realloc (LS_RYSTEP(ls), nregions, TY_INT)
+
+ # Resize the statistics buffers.
+ call realloc (LS_RMEAN(ls), nregions, TY_REAL)
+ call realloc (LS_RMEDIAN(ls), nregions, TY_REAL)
+ call realloc (LS_RMODE(ls), nregions, TY_REAL)
+ call realloc (LS_RSIGMA(ls), nregions, TY_REAL)
+ call realloc (LS_RSKY(ls), nregions, TY_REAL)
+ call realloc (LS_RSKYERR(ls), nregions, TY_REAL)
+ call realloc (LS_RMAG(ls), nregions, TY_REAL)
+ call realloc (LS_RMAGERR(ls), nregions, TY_REAL)
+ call realloc (LS_RNPTS(ls), nregions, TY_INT)
+
+ call realloc (LS_IMEAN(ls), nregions, TY_REAL)
+ call realloc (LS_IMEDIAN(ls), nregions, TY_REAL)
+ call realloc (LS_IMODE(ls), nregions, TY_REAL)
+ call realloc (LS_ISIGMA(ls), nregions, TY_REAL)
+ call realloc (LS_ISKY(ls), nregions, TY_REAL)
+ call realloc (LS_ISKYERR(ls), nregions, TY_REAL)
+ call realloc (LS_IMAG(ls), nregions, TY_REAL)
+ call realloc (LS_IMAGERR(ls), nregions, TY_REAL)
+ call realloc (LS_INPTS(ls), nregions, TY_INT)
+
+ # Resize the answers buffers.
+ call realloc (LS_RBSCALE(ls), nregions, TY_REAL)
+ call realloc (LS_RBSCALEERR(ls), nregions, TY_REAL)
+ call realloc (LS_RBZERO(ls), nregions, TY_REAL)
+ call realloc (LS_RBZEROERR(ls), nregions, TY_REAL)
+ call realloc (LS_RDELETE(ls), nregions, TY_INT)
+ call realloc (LS_RCHI(ls), nregions, TY_REAL)
+
+ # Reinitialize the region defintions.
+ call amovki (INDEFI, Memi[LS_RC1(ls)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[LS_RC2(ls)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[LS_RL1(ls)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[LS_RL2(ls)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[LS_RXSTEP(ls)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[LS_RYSTEP(ls)+nr], nregions - nr)
+
+ # Reinitialize the statistics buffers.
+ call amovkr (INDEFR, Memr[LS_RMEAN(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RMEDIAN(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RMODE(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RSIGMA(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RSKY(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RSKYERR(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RMAG(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RMAGERR(ls)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[LS_RNPTS(ls)+nr], nregions - nr)
+
+ call amovkr (INDEFR, Memr[LS_IMEAN(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_IMEDIAN(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_IMODE(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_ISIGMA(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_ISKY(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_ISKYERR(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_IMAG(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_IMAGERR(ls)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[LS_INPTS(ls)+nr], nregions - nr)
+
+ # Reinitialize the answers buffers.
+ call amovkr (INDEFR, Memr[LS_RBSCALE(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RBSCALEERR(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RBZERO(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RBZEROERR(ls)+nr], nregions - nr)
+ call amovki (LS_NO, Memi[LS_RDELETE(ls)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[LS_RCHI(ls)+nr], nregions - nr)
+end
+
+
+# RG_LRFREE -- Free the regions portion of the linscale structure.
+
+procedure rg_lrfree (ls)
+
+pointer ls #I pointer to the intensity scaling structure
+
+begin
+ LS_NREGIONS(ls) = 0
+
+ # Free the regions definitions buffers.
+ if (LS_RC1(ls) != NULL)
+ call mfree (LS_RC1(ls), TY_INT)
+ LS_RC1(ls) = NULL
+ if (LS_RC2(ls) != NULL)
+ call mfree (LS_RC2(ls), TY_INT)
+ LS_RC2(ls) = NULL
+ if (LS_RL1(ls) != NULL)
+ call mfree (LS_RL1(ls), TY_INT)
+ LS_RL1(ls) = NULL
+ if (LS_RL2(ls) != NULL)
+ call mfree (LS_RL2(ls), TY_INT)
+ LS_RL2(ls) = NULL
+ if (LS_RXSTEP(ls) != NULL)
+ call mfree (LS_RXSTEP(ls), TY_INT)
+ LS_RXSTEP(ls) = NULL
+ if (LS_RYSTEP(ls) != NULL)
+ call mfree (LS_RYSTEP(ls), TY_INT)
+ LS_RYSTEP(ls) = NULL
+
+ # Free the statistics buffers.
+ if (LS_RBUF(ls) != NULL)
+ call mfree (LS_RBUF(ls), TY_REAL)
+ if (LS_RMEAN(ls) != NULL)
+ call mfree (LS_RMEAN(ls), TY_REAL)
+ LS_RMEAN(ls) = NULL
+ if (LS_RMEDIAN(ls) != NULL)
+ call mfree (LS_RMEDIAN(ls), TY_REAL)
+ LS_RMEDIAN(ls) = NULL
+ if (LS_RMODE(ls) != NULL)
+ call mfree (LS_RMODE(ls), TY_REAL)
+ LS_RMODE(ls) = NULL
+ if (LS_RSIGMA(ls) != NULL)
+ call mfree (LS_RSIGMA(ls), TY_REAL)
+ LS_RSIGMA(ls) = NULL
+ if (LS_RSKY(ls) != NULL)
+ call mfree (LS_RSKY(ls), TY_REAL)
+ LS_RSKY(ls) = NULL
+ if (LS_RSKYERR(ls) != NULL)
+ call mfree (LS_RSKYERR(ls), TY_REAL)
+ LS_RSKYERR(ls) = NULL
+ if (LS_RMAG(ls) != NULL)
+ call mfree (LS_RMAG(ls), TY_REAL)
+ LS_RMAG(ls) = NULL
+ if (LS_RMAGERR(ls) != NULL)
+ call mfree (LS_RMAGERR(ls), TY_REAL)
+ LS_RMAGERR(ls) = NULL
+ if (LS_RNPTS(ls) != NULL)
+ call mfree (LS_RNPTS(ls), TY_INT)
+ LS_RNPTS(ls) = NULL
+
+ if (LS_IBUF(ls) != NULL)
+ call mfree (LS_IBUF(ls), TY_REAL)
+ if (LS_IMEAN(ls) != NULL)
+ call mfree (LS_IMEAN(ls), TY_REAL)
+ LS_IMEAN(ls) = NULL
+ if (LS_IMEDIAN(ls) != NULL)
+ call mfree (LS_IMEDIAN(ls), TY_REAL)
+ LS_IMEDIAN(ls) = NULL
+ if (LS_IMODE(ls) != NULL)
+ call mfree (LS_IMODE(ls), TY_REAL)
+ LS_IMODE(ls) = NULL
+ if (LS_ISIGMA(ls) != NULL)
+ call mfree (LS_ISIGMA(ls), TY_REAL)
+ LS_ISIGMA(ls) = NULL
+ if (LS_ISKY(ls) != NULL)
+ call mfree (LS_ISKY(ls), TY_REAL)
+ LS_ISKY(ls) = NULL
+ if (LS_ISKYERR(ls) != NULL)
+ call mfree (LS_ISKYERR(ls), TY_REAL)
+ LS_ISKYERR(ls) = NULL
+ if (LS_IMAG(ls) != NULL)
+ call mfree (LS_IMAG(ls), TY_REAL)
+ LS_IMAG(ls) = NULL
+ if (LS_IMAGERR(ls) != NULL)
+ call mfree (LS_IMAGERR(ls), TY_REAL)
+ LS_IMAGERR(ls) = NULL
+ if (LS_INPTS(ls) != NULL)
+ call mfree (LS_INPTS(ls), TY_INT)
+ LS_INPTS(ls) = NULL
+
+ # Free the answers buffers.
+ if (LS_RBSCALE(ls) != NULL)
+ call mfree (LS_RBSCALE(ls), TY_REAL)
+ LS_RBSCALE(ls) = NULL
+ if (LS_RBSCALEERR(ls) != NULL)
+ call mfree (LS_RBSCALEERR(ls), TY_REAL)
+ LS_RBSCALEERR(ls) = NULL
+ if (LS_RBZERO(ls) != NULL)
+ call mfree (LS_RBZERO(ls), TY_REAL)
+ LS_RBZERO(ls) = NULL
+ if (LS_RBZEROERR(ls) != NULL)
+ call mfree (LS_RBZEROERR(ls), TY_REAL)
+ LS_RBZEROERR(ls) = NULL
+ if (LS_RDELETE(ls) != NULL)
+ call mfree (LS_RDELETE(ls), TY_INT)
+ LS_RDELETE(ls) = NULL
+ if (LS_RCHI(ls) != NULL)
+ call mfree (LS_RCHI(ls), TY_REAL)
+ LS_RCHI(ls) = NULL
+end
+
+
+# RG_LFREE -- Free the linscale structure.
+
+procedure rg_lfree (ls)
+
+pointer ls #I/O pointer to the intensity scaling structure
+
+begin
+ # Free the regions dependent pointers.
+ call rg_lrfree (ls)
+
+ call mfree (ls, TY_STRUCT)
+end
+
+
+# RG_LSTATI -- Fetch the value of an integer parameter.
+
+int procedure rg_lstati (ls, param)
+
+pointer ls #I pointer to the intensity scaling structure
+int param #I parameter to be fetched
+
+begin
+ switch (param) {
+ case CNREGION:
+ return (LS_CNREGION(ls))
+ case NREGIONS:
+ return (LS_NREGIONS(ls))
+ case MAXNREGIONS:
+ return (LS_MAXNREGIONS(ls))
+ case BZALGORITHM:
+ return (LS_BZALGORITHM(ls))
+ case BSALGORITHM:
+ return (LS_BSALGORITHM(ls))
+ case DNX:
+ return (LS_DNX(ls))
+ case DNY:
+ return (LS_DNY(ls))
+ case MAXITER:
+ return (LS_MAXITER(ls))
+ case NREJECT:
+ return (LS_NREJECT(ls))
+ default:
+ call error (0, "RG_LSTATI: Unknown integer parameter.")
+ }
+end
+
+
+# RG_LSTATP -- Fetch the value of a pointer parameter.
+
+pointer procedure rg_lstatp (ls, param)
+
+pointer ls #I pointer to the intensity scaling structure
+int param #I parameter to be fetched
+
+begin
+ switch (param) {
+
+ case RC1:
+ return (LS_RC1(ls))
+ case RC2:
+ return (LS_RC2(ls))
+ case RL1:
+ return (LS_RL1(ls))
+ case RL2:
+ return (LS_RL2(ls))
+ case RXSTEP:
+ return (LS_RXSTEP(ls))
+ case RYSTEP:
+ return (LS_RYSTEP(ls))
+
+ case RBUF:
+ return (LS_RBUF(ls))
+ case RMEAN:
+ return (LS_RMEAN(ls))
+ case RMEDIAN:
+ return (LS_RMEDIAN(ls))
+ case RMODE:
+ return (LS_RMODE(ls))
+ case RSIGMA:
+ return (LS_RSIGMA(ls))
+ case RSKY:
+ return (LS_RSKY(ls))
+ case RSKYERR:
+ return (LS_RSKYERR(ls))
+ case RMAG:
+ return (LS_RMAG(ls))
+ case RMAGERR:
+ return (LS_RMAGERR(ls))
+ case RNPTS:
+ return (LS_RNPTS(ls))
+
+ case IBUF:
+ return (LS_IBUF(ls))
+ case IMEAN:
+ return (LS_IMEAN(ls))
+ case IMEDIAN:
+ return (LS_IMEDIAN(ls))
+ case IMODE:
+ return (LS_IMODE(ls))
+ case ISIGMA:
+ return (LS_ISIGMA(ls))
+ case ISKY:
+ return (LS_ISKY(ls))
+ case ISKYERR:
+ return (LS_ISKYERR(ls))
+ case IMAG:
+ return (LS_IMAG(ls))
+ case IMAGERR:
+ return (LS_IMAGERR(ls))
+ case INPTS:
+ return (LS_INPTS(ls))
+
+ case RBSCALE:
+ return (LS_RBSCALE(ls))
+ case RBSCALEERR:
+ return (LS_RBSCALEERR(ls))
+ case RBZERO:
+ return (LS_RBZERO(ls))
+ case RBZEROERR:
+ return (LS_RBZEROERR(ls))
+ case RDELETE:
+ return (LS_RDELETE(ls))
+ case RCHI:
+ return (LS_RCHI(ls))
+
+ default:
+ call error (0, "RG_LSTATP: Unknown pointer parameter.")
+ }
+end
+
+
+# RG_LSTATR -- Fetch the value of a real parameter.
+
+real procedure rg_lstatr (ls, param)
+
+pointer ls #I pointer to the intensity scaling structure
+int param #I parameter to be fetched
+
+begin
+ switch (param) {
+
+ case XSHIFT:
+ return (LS_XSHIFT(ls))
+ case YSHIFT:
+ return (LS_YSHIFT(ls))
+ case SXSHIFT:
+ return (LS_SXSHIFT(ls))
+ case SYSHIFT:
+ return (LS_SYSHIFT(ls))
+
+ case CBZERO:
+ return (LS_CBZERO(ls))
+ case CBSCALE:
+ return (LS_CBSCALE(ls))
+ case DATAMIN:
+ return (LS_DATAMIN(ls))
+ case DATAMAX:
+ return (LS_DATAMAX(ls))
+ case LOREJECT:
+ return (LS_LOREJECT(ls))
+ case HIREJECT:
+ return (LS_HIREJECT(ls))
+ case GAIN:
+ return (LS_GAIN(ls))
+ case RGAIN:
+ return (LS_RGAIN(ls))
+ case IGAIN:
+ return (LS_IGAIN(ls))
+ case READNOISE:
+ return (LS_READNOISE(ls))
+ case RREADNOISE:
+ return (LS_RREADNOISE(ls))
+ case IREADNOISE:
+ return (LS_IREADNOISE(ls))
+
+ case TBZERO:
+ return (LS_TBZERO(ls))
+ case TBZEROERR:
+ return (LS_TBZEROERR(ls))
+ case TBSCALE:
+ return (LS_TBSCALE(ls))
+ case TBSCALEERR:
+ return (LS_TBSCALEERR(ls))
+
+ default:
+ call error (0, "RG_LSTATR: Unknown real parameter.")
+ }
+end
+
+
+# RG_LSTATS -- Fetch the value of a string parameter.
+
+procedure rg_lstats (ls, param, str, maxch)
+
+pointer ls #I pointer to the intensity scaling structure
+int param #I parameter to be fetched
+char str[ARB] #I the output string
+int maxch #I maximum number of characters
+
+begin
+ switch (param) {
+ case BZSTRING:
+ call strcpy (LS_BZSTRING(ls), str, maxch)
+ case BSSTRING:
+ call strcpy (LS_BSSTRING(ls), str, maxch)
+ case CCDGAIN:
+ call strcpy (LS_CCDGAIN(ls), str, maxch)
+ case CCDREAD:
+ call strcpy (LS_CCDREAD(ls), str, maxch)
+ case IMAGE:
+ call strcpy (LS_IMAGE(ls), str, maxch)
+ case REFIMAGE:
+ call strcpy (LS_REFIMAGE(ls), str, maxch)
+ case REGIONS:
+ call strcpy (LS_REGIONS(ls), str, maxch)
+ case DATABASE:
+ call strcpy (LS_DATABASE(ls), str, maxch)
+ case OUTIMAGE:
+ call strcpy (LS_OUTIMAGE(ls), str, maxch)
+ case SHIFTSFILE:
+ call strcpy (LS_SHIFTSFILE(ls), str, maxch)
+ case PHOTFILE:
+ call strcpy (LS_PHOTFILE(ls), str, maxch)
+ case RECORD:
+ call strcpy (LS_RECORD(ls), str, maxch)
+ default:
+ call error (0, "RG_LSTATS: Unknown string parameter.")
+ }
+end
+
+
+# RG_LSETI -- Set the value of an integer parameter.
+
+procedure rg_lseti (ls, param, value)
+
+pointer ls # pointer to the intensity scaling structure
+int param # parameter to be fetched
+int value # value of the integer parameter
+
+begin
+ switch (param) {
+
+ case NREGIONS:
+ LS_NREGIONS(ls) = value
+ case CNREGION:
+ LS_CNREGION(ls) = value
+ case MAXNREGIONS:
+ LS_MAXNREGIONS(ls) = value
+
+ case BZALGORITHM:
+ LS_BZALGORITHM(ls) = value
+ switch (value) {
+ case LS_MEAN:
+ call strcpy ("mean", LS_BZSTRING(ls), SZ_FNAME)
+ case LS_MEDIAN:
+ call strcpy ("median", LS_BZSTRING(ls), SZ_FNAME)
+ case LS_MODE:
+ call strcpy ("mode", LS_BZSTRING(ls), SZ_FNAME)
+ case LS_FIT:
+ call strcpy ("fit", LS_BZSTRING(ls), SZ_FNAME)
+ case LS_PHOTOMETRY:
+ call strcpy ("photometry", LS_BZSTRING(ls), SZ_FNAME)
+ case LS_NUMBER:
+ ;
+ case LS_FILE:
+ call strcpy ("file", LS_BZSTRING(ls), SZ_FNAME)
+ LS_BSALGORITHM(ls) = value
+ call strcpy ("file", LS_BSSTRING(ls), SZ_FNAME)
+ default:
+ LS_BZALGORITHM(ls) = LS_NUMBER
+ call strcpy ("0.0", LS_BZSTRING(ls), SZ_FNAME)
+ LS_CBZERO(ls) = 0.0
+ }
+
+ case BSALGORITHM:
+ LS_BSALGORITHM(ls) = value
+ switch (value) {
+ case LS_MEAN:
+ call strcpy ("mean", LS_BSSTRING(ls), SZ_FNAME)
+ case LS_MEDIAN:
+ call strcpy ("median", LS_BSSTRING(ls), SZ_FNAME)
+ case LS_MODE:
+ call strcpy ("mode", LS_BSSTRING(ls), SZ_FNAME)
+ case LS_FIT:
+ call strcpy ("fit", LS_BSSTRING(ls), SZ_FNAME)
+ case LS_PHOTOMETRY:
+ call strcpy ("photometry", LS_BSSTRING(ls), SZ_FNAME)
+ case LS_NUMBER:
+ ;
+ case LS_FILE:
+ call strcpy ("file", LS_BSSTRING(ls), SZ_FNAME)
+ LS_BZALGORITHM(ls) = value
+ call strcpy ("file", LS_BZSTRING(ls), SZ_FNAME)
+ default:
+ LS_BSALGORITHM(ls) = LS_NUMBER
+ call strcpy ("1.0", LS_BSSTRING(ls), SZ_FNAME)
+ LS_CBSCALE(ls) = 1.0
+ }
+
+ case DNX:
+ LS_DNX(ls) = value
+ case DNY:
+ LS_DNY(ls) = value
+ case MAXITER:
+ LS_MAXITER(ls) = value
+ case NREJECT:
+ LS_NREJECT(ls) = value
+
+ default:
+ call error (0, "RG_LSETI: Unknown integer parameter.")
+ }
+end
+
+
+# RG_LSETP -- Set the value of a pointer parameter.
+
+procedure rg_lsetp (ls, param, value)
+
+pointer ls #I pointer to the linscale structure
+int param #I parameter to be fetched
+pointer value #I value of the pointer parameter
+
+begin
+ switch (param) {
+
+ case RC1:
+ LS_RC1(ls) = value
+ case RC2:
+ LS_RC2(ls) = value
+ case RL1:
+ LS_RL1(ls) = value
+ case RL2:
+ LS_RL2(ls) = value
+ case RXSTEP:
+ LS_RXSTEP(ls) = value
+ case RYSTEP:
+ LS_RYSTEP(ls) = value
+
+ case RBUF:
+ LS_RBUF(ls) = value
+ case RMEAN:
+ LS_RMEAN(ls) = value
+ case RMEDIAN:
+ LS_RMEDIAN(ls) = value
+ case RMODE:
+ LS_RMODE(ls) = value
+ case RSIGMA:
+ LS_RSIGMA(ls) = value
+ case RSKY:
+ LS_RSKY(ls) = value
+ case RSKYERR:
+ LS_RSKYERR(ls) = value
+ case RMAG:
+ LS_RMAG(ls) = value
+ case RMAGERR:
+ LS_RMAGERR(ls) = value
+ case RNPTS:
+ LS_RNPTS(ls) = value
+
+ case IBUF:
+ LS_IBUF(ls) = value
+ case IMEAN:
+ LS_IMEAN(ls) = value
+ case IMEDIAN:
+ LS_IMEDIAN(ls) = value
+ case IMODE:
+ LS_IMODE(ls) = value
+ case ISIGMA:
+ LS_ISIGMA(ls) = value
+ case ISKY:
+ LS_ISKY(ls) = value
+ case ISKYERR:
+ LS_ISKYERR(ls) = value
+ case IMAG:
+ LS_IMAG(ls) = value
+ case IMAGERR:
+ LS_IMAGERR(ls) = value
+ case INPTS:
+ LS_INPTS(ls) = value
+
+ case RBSCALE:
+ LS_RBSCALE(ls) = value
+ case RBSCALEERR:
+ LS_RBSCALEERR(ls) = value
+ case RBZERO:
+ LS_RBZERO(ls) = value
+ case RBZEROERR:
+ LS_RBZEROERR(ls) = value
+ case RDELETE:
+ LS_RDELETE(ls) = value
+ case RCHI:
+ LS_RCHI(ls) = value
+
+ default:
+ call error (0, "RG_LSETP: Unknown pointer parameter.")
+ }
+end
+
+
+# RG_LSETR -- Set the value of a real parameter.
+
+procedure rg_lsetr (ls, param, value)
+
+pointer ls #I pointer to iscale structure
+int param #I parameter to be fetched
+real value #I real parameter
+
+begin
+ switch (param) {
+ case XSHIFT:
+ LS_XSHIFT(ls) = value
+ case YSHIFT:
+ LS_YSHIFT(ls) = value
+ case SXSHIFT:
+ LS_SXSHIFT(ls) = value
+ case SYSHIFT:
+ LS_SYSHIFT(ls) = value
+ case CBZERO:
+ LS_CBZERO(ls) = value
+ case CBSCALE:
+ LS_CBSCALE(ls) = value
+ case DATAMIN:
+ LS_DATAMIN(ls) = value
+ case DATAMAX:
+ LS_DATAMAX(ls) = value
+ case LOREJECT:
+ LS_LOREJECT(ls) = value
+ case HIREJECT:
+ LS_HIREJECT(ls) = value
+ case GAIN:
+ LS_GAIN(ls) = value
+ case RGAIN:
+ LS_RGAIN(ls) = value
+ case IGAIN:
+ LS_IGAIN(ls) = value
+ case READNOISE:
+ LS_READNOISE(ls) = value
+ case RREADNOISE:
+ LS_RREADNOISE(ls) = value
+ case IREADNOISE:
+ LS_IREADNOISE(ls) = value
+ case TBSCALE:
+ LS_TBSCALE(ls) = value
+ case TBSCALEERR:
+ LS_TBSCALEERR(ls) = value
+ case TBZERO:
+ LS_TBZERO(ls) = value
+ case TBZEROERR:
+ LS_TBZEROERR(ls) = value
+ default:
+ call error (0, "RG_LSETR: Unknown real parameter.")
+ }
+end
+
+
+# RG_LSETS -- Set the value of a string parameter.
+
+procedure rg_lsets (ls, param, str)
+
+pointer ls # pointer to the intensity scaling structure
+int param # parameter to be fetched
+char str[ARB] # output string
+
+int index, ip
+pointer sp, temp
+real rval
+int fnldir(), strdic(), ctor(), rg_lstati()
+
+begin
+ call smark (sp)
+ call salloc (temp, SZ_LINE, TY_CHAR)
+
+ switch (param) {
+
+ case BZSTRING:
+ ip = 1
+ index = strdic (str, str, SZ_LINE, LS_SCALING)
+ if (index > 0) {
+ if (rg_lstati (ls, BSALGORITHM) == LS_NUMBER) {
+ call strcpy (str, LS_BZSTRING(ls), SZ_FNAME)
+ call rg_lseti (ls, BZALGORITHM, index)
+ } else {
+ call strcpy (LS_BSSTRING(ls), LS_BZSTRING(ls), SZ_FNAME)
+ call rg_lseti (ls, BZALGORITHM, rg_lstati (ls, BSALGORITHM))
+ }
+ } else if (ctor (str, ip, rval) > 0) {
+ call strcpy (str, LS_BZSTRING(ls), SZ_FNAME)
+ call rg_lsetr (ls, CBZERO, rval)
+ call rg_lseti (ls, BZALGORITHM, LS_NUMBER)
+ } else {
+ call strcpy ("0.0", LS_BZSTRING(ls), SZ_FNAME)
+ call rg_lsetr (ls, CBZERO, 0.0)
+ call rg_lseti (ls, BZALGORITHM, LS_NUMBER)
+ }
+ case BSSTRING:
+ ip = 1
+ index = strdic (str, str, SZ_LINE, LS_SCALING)
+ if (index > 0) {
+ call strcpy (str, LS_BSSTRING(ls), SZ_FNAME)
+ call rg_lseti (ls, BSALGORITHM, index)
+ } else if (ctor (str, ip, rval) > 0) {
+ call strcpy (str, LS_BSSTRING(ls), SZ_FNAME)
+ call rg_lsetr (ls, CBSCALE, rval)
+ call rg_lseti (ls, BSALGORITHM, LS_NUMBER)
+ } else {
+ call strcpy ("1.0", LS_BSSTRING(ls), SZ_FNAME)
+ call rg_lsetr (ls, CBSCALE, 1.0)
+ call rg_lseti (ls, BSALGORITHM, LS_NUMBER)
+ }
+ case CCDGAIN:
+ ip = 1
+ if (ctor (str, ip, rval) > 0) {
+ call strcpy (str, LS_CCDGAIN(ls), SZ_FNAME)
+ call rg_lsetr (ls, RGAIN, rval)
+ if (ctor (str, ip, rval) > 0)
+ call rg_lsetr (ls, IGAIN, rval)
+ else
+ call rg_lsetr (ls, IGAIN, 1.0)
+ call rg_lsetr (ls, GAIN, INDEFR)
+ } else {
+ call sscan (str)
+ call gargwrd (Memc[temp], SZ_LINE)
+ call strcpy (Memc[temp], LS_CCDGAIN(ls), SZ_FNAME)
+ call rg_lsetr (ls, RGAIN, 1.0)
+ call rg_lsetr (ls, IGAIN, 1.0)
+ call rg_lsetr (ls, GAIN, INDEFR)
+ }
+ case CCDREAD:
+ ip = 1
+ if (ctor (str, ip, rval) > 0) {
+ call strcpy (str, LS_CCDREAD(ls), SZ_FNAME)
+ call rg_lsetr (ls, RREADNOISE, rval)
+ if (ctor (str, ip, rval) > 0)
+ call rg_lsetr (ls, IREADNOISE, rval)
+ else
+ call rg_lsetr (ls, IREADNOISE, 0.0)
+ call rg_lsetr (ls, READNOISE, INDEFR)
+ } else {
+ call sscan (str)
+ call gargwrd (Memc[temp], SZ_LINE)
+ call strcpy (Memc[temp], LS_CCDREAD(ls), SZ_FNAME)
+ call rg_lsetr (ls, RREADNOISE, 0.0)
+ call rg_lsetr (ls, IREADNOISE, 0.0)
+ call rg_lsetr (ls, READNOISE, INDEFR)
+ }
+
+ case IMAGE:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], LS_IMAGE(ls), SZ_FNAME)
+ call strcpy (Memc[temp+index], LS_IMAGE(ls), SZ_FNAME)
+ case REFIMAGE:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], LS_REFIMAGE(ls), SZ_FNAME)
+ call strcpy (Memc[temp+index], LS_REFIMAGE(ls), SZ_FNAME)
+ case REGIONS:
+ call strcpy (str, LS_REGIONS(ls), SZ_FNAME)
+ case DATABASE:
+ index = fnldir (str, LS_DATABASE(ls), SZ_FNAME)
+ call strcpy (str[index+1], LS_DATABASE(ls), SZ_FNAME)
+ case OUTIMAGE:
+ call strcpy (str, LS_OUTIMAGE(ls), SZ_FNAME)
+ case SHIFTSFILE:
+ call strcpy (str, LS_SHIFTSFILE(ls), SZ_FNAME)
+ case PHOTFILE:
+ call strcpy (str, LS_PHOTFILE(ls), SZ_FNAME)
+ case RECORD:
+ call strcpy (str, LS_RECORD(ls), SZ_FNAME)
+
+ default:
+ call error (0, "RG_LSETS: Unknown string parameter.")
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/linmatch/t_linmatch.x b/pkg/images/immatch/src/linmatch/t_linmatch.x
new file mode 100644
index 00000000..d48f2c03
--- /dev/null
+++ b/pkg/images/immatch/src/linmatch/t_linmatch.x
@@ -0,0 +1,544 @@
+include <fset.h>
+include <imhdr.h>
+include <imset.h>
+include <error.h>
+include "linmatch.h"
+
+# T_LINMATCH -- Compute the parameters required to match the intensity scale
+# of an image to that of a reference image using an expression of the form
+# I(ref) = a + b * I(image)
+
+procedure t_linmatch()
+
+pointer freglist #I pointer to reference regions list
+pointer database #I pointer to database file
+int dformat #I write the output file in database format
+int interactive #I interactive mode ?
+int verbose #I verbose mode
+
+int list1, listr, list2, reglist, reclist, stat, nregions, shiftslist
+int rpfd, ipfd, sfd
+pointer sp, reference, imager, image1, imtemp, image2, str, str1, shifts
+pointer ls, db, gd, id, imr, im1, im2
+bool clgetb()
+int imtopen(), fntopnb(), imtlen(), fntlenb(), access(), btoi(), open()
+int rg_lstati(), imtgetim(), fntgfnb(), rg_lregions(), rg_lscale()
+int rg_lrphot(), rg_liscale()
+pointer dtmap(), gopen(), immap()
+real rg_lstatr()
+errchk gopen()
+
+begin
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Allocate temporary space.
+ call smark (sp)
+
+ call salloc (reference, SZ_FNAME, TY_CHAR)
+ call salloc (freglist, SZ_LINE, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (image2, SZ_FNAME, TY_CHAR)
+ call salloc (imtemp, SZ_FNAME, TY_CHAR)
+ call salloc (database, SZ_FNAME, TY_CHAR)
+ call salloc (shifts, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (str1, SZ_LINE, TY_CHAR)
+
+ # Open the input and output image lists.
+ call clgstr ("input", Memc[str], SZ_LINE)
+ list1 = imtopen (Memc[str])
+ call clgstr ("reference", Memc[reference], SZ_LINE)
+ call clgstr ("regions", Memc[freglist], SZ_LINE)
+ call clgstr ("lintransform", Memc[database], SZ_LINE)
+ call clgstr ("output", Memc[str], SZ_LINE)
+ list2 = imtopen (Memc[str])
+ call clgstr ("records", Memc[str], SZ_LINE)
+ if (Memc[str] == EOS)
+ reclist = NULL
+ else
+ reclist = fntopnb (Memc[str], NO)
+ call clgstr ("shifts", Memc[shifts], SZ_LINE)
+
+
+ # Open the cross correlation fitting structure.
+ call rg_glpars (ls)
+
+ # Test the reference image list length
+ if ((rg_lstati (ls, BZALGORITHM) == LS_FILE || rg_lstati(ls,
+ BSALGORITHM) == LS_FILE) || (rg_lstati(ls, BZALGORITHM) ==
+ LS_NUMBER && rg_lstati(ls, BSALGORITHM) == LS_NUMBER)) {
+ listr = NULL
+ reglist = NULL
+ shiftslist = NULL
+ call rg_lsets (ls, REGIONS, "")
+ } else if (rg_lstati(ls, BZALGORITHM) == LS_PHOTOMETRY || rg_lstati (ls,
+ BSALGORITHM) == LS_PHOTOMETRY) {
+ listr = fntopnb (Memc[reference], NO)
+ if (fntlenb (listr) <= 0)
+ call error (0, "The reference photometry list is empty.")
+ reglist = fntopnb (Memc[freglist], NO)
+ if (fntlenb (listr) > 1 && fntlenb (listr) != imtlen (list1)) {
+ call eprintf ("The number of reference photometry files")
+ call eprintf (" and input images is not the same.\n")
+ call erract (EA_FATAL)
+ }
+ if (fntlenb(reglist) != imtlen(list1)) {
+ call eprintf ("The number of input photometry files and")
+ call eprintf ("images are not the same.\n")
+ call erract (EA_FATAL)
+ }
+ shiftslist = NULL
+ call rg_lsets (ls, REGIONS, Memc[freglist])
+ } else {
+ listr = imtopen (Memc[reference])
+ if (imtlen (listr) <= 0)
+ call error (0, "The reference image list is empty.")
+ if (imtlen (listr) > 1 && imtlen (listr) != imtlen (list1))
+ call error (0,
+ "The number of reference and input images is not the same.")
+ iferr {
+ reglist = fntopnb (Memc[freglist], NO)
+ } then
+ reglist = NULL
+ if (Memc[shifts] == EOS)
+ shiftslist = NULL
+ else {
+ shiftslist = fntopnb (Memc[shifts], NO)
+ if (imtlen(listr) != fntlenb (shiftslist))
+ call error (0,
+ "The number of shifts files and images is not the same.")
+ }
+ call rg_lsets (ls, REGIONS, Memc[freglist])
+ }
+
+
+ # Close the output image list if it is empty.
+ if (imtlen (list2) <= 0) {
+ call imtclose (list2)
+ list2 = NULL
+ }
+
+ # Check that the output image list is the same as the input image
+ # list.
+ if (list2 != NULL) {
+ if (imtlen (list1) != imtlen (list2))
+ call error (0,
+ "The number of input and output images are not the same.")
+ }
+
+ # Check that the record list is the same length as the input image
+ # list length.
+ if (reclist != NULL) {
+ if (fntlenb (reclist) != imtlen (list1))
+ call error (0,
+ "Input image and record lists are not the same length")
+ }
+
+ # Open the database file.
+ dformat = btoi (clgetb ("databasefmt"))
+ if (rg_lstati(ls, BZALGORITHM) == LS_FILE && rg_lstati(ls,
+ BSALGORITHM) == LS_FILE) {
+ if (dformat == YES)
+ db = dtmap (Memc[database], READ_ONLY)
+ else
+ db = open (Memc[database], READ_ONLY, TEXT_FILE)
+ } else if (clgetb ("append")) {
+ if (dformat == YES)
+ db = dtmap (Memc[database], APPEND)
+ else
+ db = open (Memc[database], NEW_FILE, TEXT_FILE)
+ } else if (access(Memc[database], 0, 0) == YES) {
+ call error (0, "The shifts database file already exists")
+ } else {
+ if (dformat == YES)
+ db = dtmap (Memc[database], NEW_FILE)
+ else
+ db = open (Memc[database], NEW_FILE, TEXT_FILE)
+ }
+ call rg_lsets (ls, DATABASE, Memc[database])
+
+ if ((rg_lstati(ls, BZALGORITHM) == LS_FILE || rg_lstati(ls,
+ BSALGORITHM) == LS_FILE) || (rg_lstati(ls, BZALGORITHM) ==
+ LS_NUMBER && rg_lstati(ls, BSALGORITHM) == LS_NUMBER))
+ interactive = NO
+ else
+ interactive = btoi (clgetb ("interactive"))
+ if (interactive == YES) {
+ call clgstr ("graphics", Memc[str], SZ_FNAME)
+ iferr (gd = gopen (Memc[str], NEW_FILE, STDGRAPH))
+ gd = NULL
+ call clgstr ("display", Memc[str], SZ_FNAME)
+ iferr (id = gopen (Memc[str], APPEND, STDIMAGE))
+ id = NULL
+ verbose = YES
+ } else {
+ gd = NULL
+ id = NULL
+ verbose = btoi (clgetb ("verbose"))
+ }
+
+ # Initialize the reference image pointer.
+ imr = NULL
+ sfd = NULL
+ rpfd = NULL
+ ipfd = NULL
+
+ # Do each set of input and output images.
+ while ((imtgetim (list1, Memc[image1], SZ_FNAME) != EOF)) {
+
+ # Open the reference image and associated regions files
+ # if the correlation function is not file.
+ if (rg_lstati(ls, BZALGORITHM) == LS_PHOTOMETRY || rg_lstati(ls,
+ BSALGORITHM) == LS_PHOTOMETRY) {
+ if (fntgfnb(listr, Memc[str], SZ_FNAME) != EOF) {
+ if (rpfd != NULL)
+ call close (rpfd)
+ rpfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ call rg_lsets (ls, REFIMAGE, Memc[str])
+ call rg_lsetr (ls, RGAIN, rg_lstatr (ls,GAIN))
+ call rg_lsetr (ls, RREADNOISE, rg_lstatr (ls,READNOISE))
+ nregions = rg_lrphot (rpfd, ls, 1, rg_lstati(ls,
+ MAXNREGIONS), YES)
+ if (nregions <= 0 && interactive == NO)
+ call error (0,
+ "The reference photometry file is empty.")
+ }
+ } else if ((rg_lstati(ls, BZALGORITHM) == LS_FILE || rg_lstati(ls,
+ BSALGORITHM) == LS_FILE) || (rg_lstati(ls,BZALGORITHM) ==
+ LS_NUMBER && rg_lstati(ls,BSALGORITHM) == LS_NUMBER)) {
+ call rg_lsets (ls, REFIMAGE, "reference")
+ } else {
+ if (imtgetim(listr, Memc[str], SZ_FNAME) != EOF) {
+ if (imr != NULL)
+ call imunmap (imr)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ if (IM_NDIM(imr) > 2)
+ call error (0, "Referenc image must be 1D or 2D")
+ call rg_lgain (imr, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,GAIN)))
+ call rg_lsetr (ls, RGAIN, rg_lstatr (ls,GAIN))
+ call rg_lrdnoise (imr, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,READNOISE)))
+ call rg_lsetr (ls, RREADNOISE, rg_lstatr (ls,READNOISE))
+ call rg_lsets (ls, REFIMAGE, Memc[str])
+ nregions = rg_lregions (reglist, imr, ls, 1, NO)
+ if (nregions <= 0 && interactive == NO)
+ call error (0, "The regions list is empty.")
+ if (shiftslist != NULL) {
+ if (sfd != NULL)
+ call close (sfd)
+ if (fntgfnb (shiftslist, Memc[str], SZ_FNAME) == EOF) {
+ call rg_lsets (ls, SHIFTSFILE, "")
+ sfd = NULL
+ } else {
+ call rg_lsets (ls, SHIFTSFILE, Memc[str])
+ sfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ }
+ }
+ }
+ }
+
+ # Open the input image.
+ if (list2 == NULL && imr == NULL)
+ im1 = NULL
+ else {
+ im1 = immap (Memc[image1], READ_ONLY, 0)
+ if (IM_NDIM(im1) > 2) {
+ call error (0, "Input images must be 1D or 2D")
+ } else if (imr != NULL) {
+ if (IM_NDIM(im1) != IM_NDIM(imr)) {
+ call eprintf ("Input images must have same")
+ call eprintf (" dimensionality as reference images.\n")
+ call erract (EA_FATAL)
+ }
+ }
+ call rg_lgain (im1, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,GAIN)))
+ call rg_lsetr (ls, IGAIN, rg_lstatr (ls, GAIN))
+ call rg_lrdnoise (im1, ls)
+ if (!IS_INDEFR(rg_lstatr(ls,READNOISE)))
+ call rg_lsetr (ls, IREADNOISE, rg_lstatr (ls, READNOISE))
+ }
+ call rg_lsets (ls, IMAGE, Memc[image1])
+
+ # Open the input photometry file.
+ if (rpfd != NULL) {
+ if (fntgfnb (reglist, Memc[str], SZ_FNAME) != EOF) {
+ ipfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ call rg_lsets (ls, PHOTFILE, Memc[str])
+ }
+ nregions = rg_lrphot (ipfd, ls, 1, rg_lstati (ls,
+ NREGIONS), NO)
+ if (nregions <= 0 && interactive == NO)
+ call error (0,
+ "The input photometry file is empty.")
+ if (nregions < rg_lstati (ls, NREGIONS) && interactive == NO) {
+ call eprintf ("The input photometry file has fewer")
+ call eprintf (" objects than the reference photoemtry")
+ call eprintf (" file.\n")
+ call erract (EA_FATAL)
+ }
+ }
+
+ # Open the output image if any.
+ if (list2 == NULL) {
+ im2 = NULL
+ Memc[image2] = EOS
+ } else if (imtgetim (list2, Memc[image2], SZ_FNAME) != EOF) {
+ call xt_mkimtemp (Memc[image1], Memc[image2], Memc[imtemp],
+ SZ_FNAME)
+ im2 = immap (Memc[image2], NEW_COPY, im1)
+ } else {
+ im2 = NULL
+ Memc[image2] = EOS
+ }
+ call rg_lsets (ls, OUTIMAGE, Memc[image2])
+
+ # Get the record names.
+ if (reclist == NULL)
+ call strcpy (Memc[image1], Memc[str], SZ_FNAME)
+ else if (fntgfnb (reclist, Memc[str], SZ_FNAME) == EOF)
+ call strcpy (Memc[image1], Memc[str], SZ_FNAME)
+ call rg_lsets (ls, RECORD, Memc[str])
+
+ # Compute the initial shift.
+ if (sfd != NULL) {
+ call rg_lgshift (sfd, ls)
+ } else {
+ call rg_lsetr (ls, SXSHIFT, rg_lstatr (ls, XSHIFT))
+ call rg_lsetr (ls, SYSHIFT, rg_lstatr (ls, YSHIFT))
+ }
+
+ # Compute the scaling factors.
+ if (interactive == YES) {
+ stat = rg_liscale (imr, im1, im2, db, dformat, reglist,
+ rpfd, ipfd, sfd, ls, gd, id)
+ } else {
+ stat = rg_lscale (imr, im1, db, dformat, ls)
+ if (verbose == YES) {
+ if (rg_lstati(ls,BSALGORITHM) == LS_PHOTOMETRY ||
+ rg_lstati(ls,BZALGORITHM) == LS_PHOTOMETRY)
+ call rg_lstats (ls, PHOTFILE, Memc[str1], SZ_FNAME)
+ else
+ call strcpy (Memc[image1], Memc[str1], SZ_FNAME)
+ call rg_lstats (ls, REFIMAGE, Memc[str], SZ_LINE)
+ call printf (
+ "Average scale factors from %s to %s are %g %g\n")
+ call pargstr (Memc[str1])
+ call pargstr (Memc[str])
+ call pargr (rg_lstatr (ls, TBSCALE))
+ call pargr (rg_lstatr (ls, TBZERO))
+ }
+ }
+
+ # Scale the image.
+ if (im2 != NULL && stat == NO) {
+ if (verbose == YES) {
+ call printf (
+ "\tScaling image %s to image %s ...\n")
+ call pargstr (Memc[image1])
+ call pargstr (Memc[imtemp])
+ }
+ call imseti (im1, IM_CANCEL, YES)
+ call rg_limscale (im1, im2, rg_lstatr (ls, TBSCALE),
+ rg_lstatr (ls, TBZERO))
+ }
+
+ # Close up the input and output images.
+ if (im1 != NULL)
+ call imunmap (im1)
+ if (im2 != NULL) {
+ call imunmap (im2)
+ if (stat == YES)
+ call imdelete (Memc[image2])
+ else
+ call xt_delimtemp (Memc[image2], Memc[imtemp])
+ }
+
+ if (stat == YES)
+ break
+ }
+
+ # Close up the files and images.
+ if (imr != NULL)
+ call imunmap (imr)
+
+ # Close up the lists.
+ if (list1 != NULL)
+ call imtclose (list1)
+ if (listr != NULL) {
+ if (rg_lstati (ls, BZALGORITHM) == LS_PHOTOMETRY || rg_lstati(ls,
+ BSALGORITHM) == LS_PHOTOMETRY)
+ call fntclsb (listr)
+ else
+ call imtclose (listr)
+ }
+ if (list2 != NULL)
+ call imtclose (list2)
+ if (sfd != NULL)
+ call close (sfd)
+ if (rpfd != NULL)
+ call close (rpfd)
+ if (ipfd != NULL)
+ call close (ipfd)
+ if (shiftslist != NULL)
+ call fntclsb (shiftslist)
+ if (reglist != NULL)
+ call fntclsb (reglist)
+ if (reclist != NULL)
+ call fntclsb (reclist)
+ if (dformat == YES)
+ call dtunmap (db)
+ else
+ call close (db)
+
+ # Close up the graphics and image display devices.
+ if (gd != NULL)
+ call gclose (gd)
+ if (id != NULL)
+ call gclose (id)
+
+ # Free the matching structure.
+ call rg_lfree (ls)
+
+ call sfree (sp)
+end
+
+
+# RG_LGAIN -- Fetch the gain parameter from the image header.
+
+procedure rg_lgain (im, ls)
+
+pointer im #I pointer to the input image
+pointer ls #I pointer to the intensity matching structure
+
+int ip
+pointer sp, key
+real epadu
+int ctor()
+real imgetr()
+errchk imgetr()
+
+begin
+ call smark (sp)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+
+ call rg_lstats (ls, CCDGAIN, Memc[key], SZ_FNAME)
+ ip = 1
+ if (ctor (Memc[key], ip, epadu) <= 0) {
+ iferr {
+ epadu = imgetr (im, Memc[key])
+ } then {
+ epadu = INDEFR
+ call eprintf ("Warning: Image %s Keyword %s not found.\n")
+ call pargstr (IM_HDRFILE(im))
+ call pargstr (Memc[key])
+ }
+ } else
+ epadu = INDEFR
+ if (IS_INDEFR(epadu) || epadu <= 0.0)
+ call rg_lsetr (ls, GAIN, INDEFR)
+ Else
+ call rg_lsetr (ls, GAIN, epadu)
+
+ call sfree (sp)
+end
+
+
+# LG_LRDNOISE -- Fetch the readout noise from the image header.
+
+procedure rg_lrdnoise (im, ls)
+
+pointer im #I pointer to the input image
+pointer ls #I pointer to the intensity matching structure
+
+int ip
+pointer sp, key
+real rdnoise
+int ctor()
+real imgetr()
+errchk imgetr()
+
+begin
+ call smark (sp)
+ call salloc (key, SZ_FNAME, TY_CHAR)
+
+ call rg_lstats (ls, CCDREAD, Memc[key], SZ_FNAME)
+ ip = 1
+ if (ctor (Memc[key], ip, rdnoise) <= 0) {
+ iferr {
+ rdnoise = imgetr (im, Memc[key])
+ } then {
+ rdnoise = INDEFR
+ call eprintf ("Warning: Image %s Keyword %s not found.\n")
+ call pargstr (IM_HDRFILE(im))
+ call pargstr (Memc[key])
+ }
+ } else
+ rdnoise = INDEFR
+ if (IS_INDEFR(rdnoise) || rdnoise <= 0.0)
+ call rg_lsetr (ls, READNOISE, INDEFR)
+ else
+ call rg_lsetr (ls, READNOISE, rdnoise)
+
+ call sfree (sp)
+end
+
+
+# RG_LGSHIFT -- Read the x and y shifts from a file
+
+procedure rg_lgshift (fd, ls)
+
+int fd #I input shifts file descriptor
+pointer ls #I pointer to the intensity matching structure
+
+real xshift, yshift
+int fscan(), nscan()
+
+begin
+ xshift = 0.0
+ yshift = 0.0
+
+ while (fscan(fd) != EOF) {
+ call gargr (xshift)
+ call gargr (yshift)
+ if (nscan() >= 2)
+ break
+ xshift = 0.0
+ yshift = 0.0
+ }
+
+ call rg_lsetr (ls, SXSHIFT, xshift)
+ call rg_lsetr (ls, SYSHIFT, yshift)
+end
+
+
+# RG_LIMSCALE -- Linearly scale the input image.
+
+procedure rg_limscale (im1, im2, bscale, bzero)
+
+pointer im1 #I pointer to the input image
+pointer im2 #I pointer to the output image
+real bscale #I the bscale value
+real bzero #I the bzero value
+
+int ncols
+pointer sp, v1, v2, buf1, buf2
+int imgnlr(), impnlr()
+
+begin
+ call smark (sp)
+ call salloc (v1, IM_MAXDIM, TY_LONG)
+ call salloc (v2, IM_MAXDIM, TY_LONG)
+
+ ncols = IM_LEN(im1,1)
+ call amovkl (long(1), Meml[v1], IM_MAXDIM)
+ call amovkl (long(1), Meml[v2], IM_MAXDIM)
+ while (imgnlr (im1, buf1, Meml[v1]) != EOF) {
+ if (impnlr (im2, buf2, Meml[v2]) != EOF)
+ call altmr (Memr[buf1], Memr[buf2], ncols, bscale, bzero)
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/listmatch/mkpkg b/pkg/images/immatch/src/listmatch/mkpkg
new file mode 100644
index 00000000..1d9f42c5
--- /dev/null
+++ b/pkg/images/immatch/src/listmatch/mkpkg
@@ -0,0 +1,12 @@
+# Make the XYXYMATCH/IMCENTROID tasks
+
+$checkout libpkg.a ../../../
+$update libpkg.a
+$checkin libpkg.a ../../../
+$exit
+
+
+libpkg.a:
+ t_imctroid.x <error.h> <mach.h> <imhdr.h>
+ t_xyxymatch.x <fset.h> "../../../lib/xyxymatch.h"
+ ;
diff --git a/pkg/images/immatch/src/listmatch/t_imctroid.x b/pkg/images/immatch/src/listmatch/t_imctroid.x
new file mode 100644
index 00000000..157e41ca
--- /dev/null
+++ b/pkg/images/immatch/src/listmatch/t_imctroid.x
@@ -0,0 +1,1016 @@
+include <fset.h>
+include <imhdr.h>
+include <error.h>
+include <mach.h>
+
+define LEN_CP 32 # center structure pointer
+
+# task parameters
+define SMALLBOX Memi[($1)]
+define BIGBOX Memi[($1)+1]
+define VERBOSE Memi[($1)+2]
+define NEGATIVE Memi[($1)+3]
+define BACKGROUND Memr[P2R(($1)+4)]
+define LO_THRESH Memr[P2R(($1)+5)]
+define HI_THRESH Memr[P2R(($1)+6)]
+define MAX_TRIES Memi[($1)+7]
+define TOL Memi[($1)+8]
+define MAX_SHIFT Memr[P2R(($1)+9)]
+
+# other scalars
+define IM Memi[($1)+10]
+define BOXSIZE Memi[($1)+11]
+define BACK_LOCAL Memr[P2R(($1)+12)]
+define LO_LOCAL Memr[P2R(($1)+13)]
+define HI_LOCAL Memr[P2R(($1)+14)]
+define NIMAGES Memi[($1)+15]
+define NCOORDS Memi[($1)+16]
+
+# expensive, but the indexing isn't done excessively many times
+define OFF1D (($1)-1)
+define OFF2D ((($2)-1)*NCOORDS($1)+(($3)-1))
+
+# vectors and matrices
+define XINIT_PT Memi[($1)+20] # need space for NCOORDS of these
+define YINIT_PT Memi[($1)+21]
+define XINIT Memr[XINIT_PT($1)+OFF1D($2)]
+define YINIT Memr[YINIT_PT($1)+OFF1D($2)]
+
+define XSHIFT_PT Memi[($1)+22] # space for NIMAGES of these
+define YSHIFT_PT Memi[($1)+23]
+define XSHIFT Memr[XSHIFT_PT($1)+OFF1D($2)]
+define YSHIFT Memr[YSHIFT_PT($1)+OFF1D($2)]
+
+define XSIZE_PT Memi[($1)+24] # space for NIMAGES+1
+define YSIZE_PT Memi[($1)+25]
+define XSIZE Memr[XSIZE_PT($1)+OFF1D($2)]
+define YSIZE Memr[YSIZE_PT($1)+OFF1D($2)]
+
+define XCENTER_PT Memi[($1)+26] # space for (NIMAGES+1)*NCOORDS
+define YCENTER_PT Memi[($1)+27]
+define XCENTER Memr[XCENTER_PT($1)+OFF2D($1,$2,$3)]
+define YCENTER Memr[YCENTER_PT($1)+OFF2D($1,$2,$3)]
+
+define XSIGMA_PT Memi[($1)+28]
+define YSIGMA_PT Memi[($1)+29]
+define XSIGMA Memr[XSIGMA_PT($1)+OFF2D($1,$2,$3)]
+define YSIGMA Memr[YSIGMA_PT($1)+OFF2D($1,$2,$3)]
+
+define REJECTED_PT Memi[($1)+30]
+define REJECTED Memi[REJECTED_PT($1)+OFF2D($1,$2,$3)]
+
+
+# list "template" structure, currently just read the file
+define LEN_LP 2
+
+define LP_FD Memi[($1)]
+define LP_LEN Memi[($1)+1]
+
+# T_IMCENTROID -- Find the centroids of a list of sources in a list of
+# images and compute the average shifts relative to a reference image.
+
+procedure t_imcentroid()
+
+pointer imlist, coordlist, shiftlist
+pointer img, ref, refer, cp, im, sp
+int nimages, ncoords, nshifts, ncentered, i, j
+real x, y, junk
+bool error_seen, firsttime
+
+pointer imtopenp(), immap(), ia_openp2r(), ia_init()
+int imtlen(), imtgetim(), ia_len(), ia_center(), strmatch()
+
+errchk imtopenp, immap, imunmap
+errchk ia_init, ia_openp2r, ia_len, ia_close, ia_center
+
+begin
+ call smark (sp)
+ call salloc (img, SZ_FNAME, TY_CHAR)
+ call salloc (refer, SZ_FNAME, TY_CHAR)
+
+ error_seen = false
+ imlist = NULL
+ coordlist = NULL
+ shiftlist = NULL
+ ref = NULL
+ cp = NULL
+
+ iferr {
+ # Flush on new line to avoid eprint output from appear
+ # in the middle of regular output.
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Open the input image list.
+ imlist = imtopenp ("input")
+ nimages = imtlen (imlist)
+ if (nimages <= 0)
+ call error (1, "No images specified")
+
+ # Get the reference image and check name for whitespace.
+ call clgstr ("reference", Memc[refer], SZ_FNAME)
+ if (Memc[refer] != EOS && strmatch (Memc[refer], "^#$") == 0)
+ iferr (ref = immap (Memc[refer], READ_ONLY, 0)) {
+ ref = NULL
+ call error (1, "Reference not found")
+ }
+
+ # Open the coordinate list.
+ coordlist = ia_openp2r ("coords")
+ ncoords = ia_len (coordlist)
+ if (ncoords <= 0)
+ call error (1, "No coordinates found")
+
+ # Open the shifts file.
+ shiftlist = ia_openp2r ("shifts")
+ nshifts = ia_len (shiftlist)
+ if (nshifts <= 0)
+ call ia_close (shiftlist)
+ else if (nshifts != nimages)
+ call error (1, "Number of shifts doesn't match images")
+
+ # Initialize the centering structure.
+ cp = ia_init (shiftlist, nimages, coordlist, ncoords)
+
+ if (ref == NULL)
+ VERBOSE(cp) = YES
+
+ if (VERBOSE(cp) == YES) {
+ call printf ("#Coords%16tImage X-center Err")
+ call printf (" Y-center Err Num\n")
+ call flush (STDOUT)
+ }
+
+ # Loop over all the images
+ ncentered = 0
+ for (i=1; imtgetim (imlist, Memc[img], SZ_FNAME) != EOF; i=i+1) {
+ im = immap (Memc[img], READ_ONLY, 0)
+ IM(cp) = im
+
+ if (IM_NDIM(im) != 2) {
+ call eprintf ("%s: ")
+ call pargstr (Memc[img])
+ call error (1, "Image is not 2 dimensional")
+ }
+
+ XSIZE(cp,i) = real (IM_LEN(im,1))
+ YSIZE(cp,i) = real (IM_LEN(im,2))
+
+ if (nshifts == 0) {
+ BOXSIZE(cp) = BIGBOX(cp)
+ if (ia_center (cp, XINIT(cp,1), YINIT(cp,1), x, y,
+ junk, junk) == ERR)
+ call error (1, "Problem with coarse centering")
+ XSHIFT(cp,i) = XINIT(cp,1) - x
+ YSHIFT(cp,i) = YINIT(cp,1) - y
+ }
+
+ firsttime = true
+ do j = 1, ncoords {
+ x = XINIT(cp,j) - XSHIFT(cp,i)
+ y = YINIT(cp,j) - YSHIFT(cp,i)
+
+ if (x < 1 || x > XSIZE(cp,i) || y < 1 || y > YSIZE(cp,i)) {
+ REJECTED(cp,i,j) = YES
+ next
+ }
+
+ BOXSIZE(cp) = SMALLBOX(cp)
+ if (ia_center (cp, x, y, XCENTER(cp,i,j), YCENTER(cp,i,j),
+ XSIGMA(cp,i,j), YSIGMA(cp,i,j)) == ERR) {
+ REJECTED(cp,i,j) = YES
+ next
+ }
+
+ if (abs (XCENTER(cp,i,j) - x) > MAX_SHIFT(cp)) {
+ REJECTED(cp,i,j) = YES
+ next
+ }
+ if (abs (YCENTER(cp,i,j) - y) > MAX_SHIFT(cp)) {
+ REJECTED(cp,i,j) = YES
+ next
+ }
+
+ if (firsttime)
+ firsttime = false
+
+ if (VERBOSE(cp) == YES) {
+ call printf (
+ "%20s %9.3f (%.3f) %9.3f (%.3f) %4d\n")
+ call pargstr (Memc[img])
+ call pargr (XCENTER(cp,i,j))
+ call pargr (XSIGMA(cp,i,j))
+ call pargr (YCENTER(cp,i,j))
+ call pargr (YSIGMA(cp,i,j))
+ call pargi (j)
+ }
+ }
+
+ if (firsttime) {
+ call eprintf ("Warning: no sources centered in %s\n")
+ call pargstr (Memc[img])
+ call flush (STDERR)
+ } else
+ ncentered = ncentered + 1
+
+ if (VERBOSE(cp) == YES) {
+ call printf ("\n")
+ call flush (STDOUT)
+ }
+
+ call imunmap (im)
+ }
+
+ # Measure the reference coordinates if any.
+ if (ref != NULL) {
+ IM(cp) = ref
+
+ if (IM_NDIM(ref) != 2) {
+ call eprintf ("%s: ")
+ call pargstr (Memc[refer])
+ call error (1, "Reference image is not 2 dimensional")
+ }
+
+ XSIZE(cp,nimages+1) = real (IM_LEN(ref,1))
+ YSIZE(cp,nimages+1) = real (IM_LEN(ref,2))
+
+ firsttime = true
+ do j = 1, ncoords {
+ x = XINIT(cp,j)
+ y = YINIT(cp,j)
+
+ if (x < 1 || x > XSIZE(cp,nimages+1) ||
+ y < 1 || y > YSIZE(cp,nimages+1)) {
+ REJECTED(cp,nimages+1,j) = YES
+ next
+ }
+
+ BOXSIZE(cp) = SMALLBOX(cp)
+ if (ia_center (cp, x, y, XCENTER(cp,nimages+1,j),
+ YCENTER(cp,nimages+1,j), XSIGMA(cp,nimages+1,j),
+ YSIGMA(cp,nimages+1,j)) == ERR) {
+ REJECTED(cp,nimages+1,j) = YES
+ next
+ }
+
+ if (abs (XCENTER(cp,nimages+1,j) - x) > MAX_SHIFT(cp)) {
+ REJECTED(cp,nimages+1,j) = YES
+ next
+ }
+ if (abs (YCENTER(cp,nimages+1,j) - y ) > MAX_SHIFT(cp)) {
+ REJECTED(cp,nimages+1,j) = YES
+ next
+ }
+
+ if (firsttime) {
+ if (VERBOSE(cp) == YES) {
+ call printf (
+ "#Refcoords%12tReference X-center Err")
+ call printf (" Y-center Err Num\n")
+ }
+ firsttime = false
+ }
+
+ if (VERBOSE(cp) == YES) {
+ call printf (
+ "%20s %9.3f (%0.3f) %9.3f (%.3f) %4d\n")
+ call pargstr (Memc[refer])
+ call pargr (XCENTER(cp,nimages+1,j))
+ call pargr (XSIGMA(cp,nimages+1,j))
+ call pargr (YCENTER(cp,nimages+1,j))
+ call pargr (YSIGMA(cp,nimages+1,j))
+ call pargi (j)
+ }
+ }
+
+ if (firsttime) {
+ call eprintf ("Warning: no sources centered in reference\n")
+ call flush (STDERR)
+
+ } else {
+ if (VERBOSE(cp) == YES) {
+ call printf ("\n")
+ call flush (STDOUT)
+ }
+
+ call imtrew (imlist)
+ call ia_stats (cp, imlist)
+
+ if (ncentered > 1)
+ call ia_trim (cp)
+ }
+ }
+
+ } then
+ error_seen = true
+
+ call ia_free (cp)
+
+ if (shiftlist != NULL)
+ call ia_close (shiftlist)
+ if (ref != NULL)
+ call imunmap (ref)
+ if (coordlist != NULL)
+ call ia_close (coordlist)
+ if (imlist != NULL)
+ call imtclose (imlist)
+
+ call sfree (sp)
+
+ if (error_seen)
+ call erract (EA_WARN)
+end
+
+
+# IA_INIT -- Initialize the centering structure.
+
+pointer procedure ia_init (shiftlist, nshifts, coordlist, ncoords)
+
+pointer shiftlist #I shift "template" pointer
+int nshifts #I number of shifts in list (or # images)
+pointer coordlist #I coordinate "template" pointer
+int ncoords #I number of coordinates in list
+
+pointer cp
+int boxsize, i
+real x, y
+
+int clgeti(), btoi(), ia_get2r()
+real clgetr()
+bool clgetb()
+
+errchk ia_get2r
+
+begin
+ call calloc (cp, LEN_CP, TY_STRUCT)
+
+ boxsize = clgeti ("boxsize")
+ if (mod (boxsize, 2) == 0) {
+ boxsize = boxsize + 1
+ call eprintf ("Warning: boxsize must be odd, using %d\n")
+ call pargi (boxsize)
+ }
+ SMALLBOX(cp) = (boxsize - 1) / 2
+
+ if (shiftlist == NULL) {
+ boxsize = clgeti ("bigbox")
+ if (mod (boxsize, 2) == 0) {
+ boxsize = boxsize + 1
+ call eprintf ("Warning: bigbox must be odd, using %d\n")
+ call pargi (boxsize)
+ }
+ BIGBOX(cp) = (boxsize - 1) / 2
+ }
+
+ NEGATIVE(cp) = btoi (clgetb ("negative"))
+ BACKGROUND(cp) = clgetr ("background")
+
+ x = clgetr ("lower")
+ y = clgetr ("upper")
+
+ if (IS_INDEFR(x) || IS_INDEFR(y)) {
+ LO_THRESH(cp) = x
+ HI_THRESH(cp) = y
+ } else {
+ LO_THRESH(cp) = min (x, y)
+ HI_THRESH(cp) = max (x, y)
+ }
+
+ MAX_TRIES(cp) = max (clgeti ("niterate"), 2)
+ TOL(cp) = abs (clgeti ("tolerance"))
+ MAX_SHIFT(cp) = clgetr ("maxshift")
+ if (IS_INDEFR(MAX_SHIFT(cp)))
+ MAX_SHIFT(cp) = MAX_REAL
+ else
+ MAX_SHIFT(cp) = abs (MAX_SHIFT(cp))
+ VERBOSE(cp) = btoi (clgetb ("verbose"))
+
+ IM(cp) = NULL
+
+ NIMAGES(cp) = nshifts
+ NCOORDS(cp) = ncoords
+
+ call malloc (XINIT_PT(cp), ncoords, TY_REAL)
+ call malloc (YINIT_PT(cp), ncoords, TY_REAL)
+ call malloc (XSHIFT_PT(cp), nshifts, TY_REAL)
+ call malloc (YSHIFT_PT(cp), nshifts, TY_REAL)
+ call malloc (XSIZE_PT(cp), nshifts+1, TY_REAL)
+ call malloc (YSIZE_PT(cp), nshifts+1, TY_REAL)
+ call malloc (XCENTER_PT(cp), (nshifts+1)*ncoords, TY_REAL)
+ call malloc (YCENTER_PT(cp), (nshifts+1)*ncoords, TY_REAL)
+ call malloc (XSIGMA_PT(cp), (nshifts+1)*ncoords, TY_REAL)
+ call malloc (YSIGMA_PT(cp), (nshifts+1)*ncoords, TY_REAL)
+ call calloc (REJECTED_PT(cp), (nshifts+1)*ncoords, TY_INT)
+
+ for (i=1; ia_get2r (coordlist, x, y) != EOF; i=i+1) {
+ if (i > ncoords)
+ call error (1, "problem reading coordinate file")
+ XINIT(cp,i) = x
+ YINIT(cp,i) = y
+ }
+
+ for (i=1; ia_get2r (shiftlist, x, y) != EOF; i=i+1) {
+ if (i > nshifts)
+ call error (1, "problem reading shifts file")
+ XSHIFT(cp,i) = x
+ YSHIFT(cp,i) = y
+ }
+
+ return (cp)
+end
+
+
+# IA_FREE -- Free the structure pointer.
+
+procedure ia_free (cp)
+
+pointer cp #O center structure pointer
+
+begin
+ if (cp == NULL)
+ return
+
+ if (REJECTED_PT(cp) != NULL)
+ call mfree (REJECTED_PT(cp), TY_INT)
+ if (XSIGMA_PT(cp) != NULL)
+ call mfree (XSIGMA_PT(cp), TY_REAL)
+ if (YSIGMA_PT(cp) != NULL)
+ call mfree (YSIGMA_PT(cp), TY_REAL)
+ if (XCENTER_PT(cp) != NULL)
+ call mfree (XCENTER_PT(cp), TY_REAL)
+ if (YCENTER_PT(cp) != NULL)
+ call mfree (YCENTER_PT(cp), TY_REAL)
+ if (XSIZE_PT(cp) != NULL)
+ call mfree (XSIZE_PT(cp), TY_REAL)
+ if (YSIZE_PT(cp) != NULL)
+ call mfree (YSIZE_PT(cp), TY_REAL)
+ if (XSHIFT_PT(cp) != NULL)
+ call mfree (XSHIFT_PT(cp), TY_REAL)
+ if (YSHIFT_PT(cp) != NULL)
+ call mfree (YSHIFT_PT(cp), TY_REAL)
+ if (XINIT_PT(cp) != NULL)
+ call mfree (XINIT_PT(cp), TY_REAL)
+ if (YINIT_PT(cp) != NULL)
+ call mfree (YINIT_PT(cp), TY_REAL)
+
+ call mfree (cp, TY_STRUCT)
+ cp = NULL # just in case...
+end
+
+
+# IA_CENTER -- Compute star center using MPC algorithm.
+
+int procedure ia_center (cp, xinit, yinit, xcenter, ycenter, xsigma, ysigma)
+
+pointer cp #I center structure pointer
+real xinit, yinit #I initial x and y coordinates
+real xcenter, ycenter #O centered x and y coordinates
+real xsigma, ysigma #O centering errors
+
+int x1, x2, y1, y2, nx, ny, try
+pointer im, buf, xbuf, ybuf, sp
+real xold, yold, xnew, ynew
+bool converged
+
+pointer imgs2r()
+real ia_ctr1d()
+
+errchk imgs2r, ia_threshold, ia_rowsum, ia_colsum, ia_ctr1d
+
+begin
+ im = IM(cp)
+ xold = xinit
+ yold = yinit
+ converged = false
+
+ do try = 1, MAX_TRIES(cp) {
+ x1 = max (nint(xold) - BOXSIZE(cp), 1)
+ x2 = min (nint(xold) + BOXSIZE(cp), IM_LEN(im,1))
+ y1 = max (nint(yold) - BOXSIZE(cp), 1)
+ y2 = min (nint(yold) + BOXSIZE(cp), IM_LEN(im,2))
+
+ nx = x2 - x1 + 1
+ ny = y2 - y1 + 1
+
+ # inside the loop in case we're near an edge
+ call smark (sp)
+ call salloc (xbuf, nx, TY_REAL)
+ call salloc (ybuf, ny, TY_REAL)
+
+ iferr {
+ buf = imgs2r (im, x1, x2, y1, y2)
+
+ call ia_threshold (cp, Memr[buf], nx*ny)
+ call ia_rowsum (cp, Memr[buf], Memr[xbuf], nx, ny)
+ call ia_colsum (cp, Memr[buf], Memr[ybuf], nx, ny)
+
+ xnew = x1 + ia_ctr1d (Memr[xbuf], nx, xsigma)
+ ynew = y1 + ia_ctr1d (Memr[ybuf], ny, ysigma)
+ } then {
+ call sfree (sp)
+ call erract (EA_WARN)
+ return (ERR)
+ }
+
+ call sfree (sp)
+
+ if (abs (nint(xnew) - nint(xold)) <= TOL(cp) &&
+ abs (nint(ynew) - nint(yold)) <= TOL(cp)) {
+
+ converged = true
+ break
+ }
+
+ xold = xnew
+ yold = ynew
+ }
+
+ if (converged) {
+ xcenter = xnew
+ ycenter = ynew
+ return (OK)
+ } else {
+ call eprintf ("Warning: failed to converge near (%d,%d)\n")
+ call pargi (nint (xinit))
+ call pargi (nint (yinit))
+ call flush (STDERR)
+ return (ERR)
+ }
+end
+
+
+# IA_THRESHOLD -- Find the low and high thresholds for the subraster.
+
+procedure ia_threshold (cp, raster, npix)
+
+pointer cp #I center structure pointer
+real raster[ARB] #I 2-D subraster
+int npix #I size of the (apparently) 1-D subraster
+
+real lo, hi, junk
+
+int awvgr()
+
+errchk alimr, awvgr
+
+begin
+ # use the local data min or max for thresholds that are INDEF.
+ if (IS_INDEFR(LO_THRESH(cp)) || IS_INDEFR(HI_THRESH(cp)))
+ call alimr (raster, npix, lo, hi)
+ if (! IS_INDEFR(LO_THRESH(cp)))
+ lo = LO_THRESH(cp)
+ if (! IS_INDEFR(HI_THRESH(cp)))
+ hi = HI_THRESH(cp)
+
+ if (IS_INDEFR(BACKGROUND(cp))) {
+ if (awvgr (raster, npix, BACK_LOCAL(cp), junk, lo, hi) <= 0)
+ call error (1, "no pixels between thresholds")
+ } else
+ BACK_LOCAL(cp) = BACKGROUND(cp)
+
+ if (NEGATIVE(cp) == YES) {
+ LO_LOCAL(cp) = lo
+ HI_LOCAL(cp) = min (hi, BACK_LOCAL(cp))
+ } else {
+ LO_LOCAL(cp) = max (lo, BACK_LOCAL(cp))
+ HI_LOCAL(cp) = hi
+ }
+end
+
+
+# IA_ROWSUM -- Sum all rows in a raster, subject to the thresholds, the
+# background, and other parameters.
+
+procedure ia_rowsum (cp, raster, row, nx, ny)
+
+pointer cp #I center structure pointer
+real raster[nx,ny] #I 2-D subraster
+real row[ARB] #O 1-D squashed row vector
+int nx, ny #I dimensions of the subraster
+
+int i, j
+real lo, hi, back, pix
+
+begin
+ call aclrr (row, nx)
+
+ back = BACK_LOCAL(cp)
+ lo = LO_LOCAL(cp)
+ hi = HI_LOCAL(cp)
+
+ do j = 1, ny
+ do i = 1, nx {
+ pix = raster[i,j]
+ if (lo <= pix && pix <= hi)
+ row[i] = row[i] + pix - back
+ }
+
+ if (NEGATIVE(cp) == YES)
+ call adivkr (row, -real(ny), row, nx)
+ else
+ call adivkr (row, real(ny), row, nx)
+
+ # recycle lo (and hi)
+ call alimr (row, nx, lo, hi)
+ if (lo < 0.)
+ call error (1, "Negative value in marginal row\n")
+end
+
+
+# IA_COLSUM -- Sum all columns in a raster, subject to the thresholds, the
+# background, and other parameters.
+
+procedure ia_colsum (cp, raster, col, nx, ny)
+
+pointer cp #I center structure pointer
+real raster[nx,ny] #I 2-D subraster
+real col[ARB] #O 1-D squashed col vector
+int nx, ny #I dimensions of the subraster
+
+int i, j
+real lo, hi, back, pix
+
+begin
+ call aclrr (col, ny)
+
+ back = BACK_LOCAL(cp)
+ lo = LO_LOCAL(cp)
+ hi = HI_LOCAL(cp)
+
+ do j = 1, ny
+ do i = 1, nx {
+ pix = raster[i,j]
+ if (lo <= pix && pix <= hi)
+ col[j] = col[j] + pix - back
+ }
+
+ if (NEGATIVE(cp) == YES)
+ call adivkr (col, -real(nx), col, ny)
+ else
+ call adivkr (col, real(nx), col, ny)
+
+ # recycle lo (and hi)
+ call alimr (col, ny, lo, hi)
+ if (lo < 0.)
+ call error (1, "Negative value in marginal column\n")
+end
+
+
+# IA_CNTR1D -- Compute the the first moment.
+
+real procedure ia_ctr1d (a, npix, err)
+
+real a[ARB] #I marginal vector
+int npix #I size of the vector
+real err #O error in the centroid
+
+real centroid, pix, sumi, sumix, sumix2
+int i
+
+bool fp_equalr()
+
+begin
+ sumi = 0.
+ sumix = 0.
+ sumix2 = 0.
+
+ do i = 1, npix {
+ pix = a[i]
+ sumi = sumi + pix
+ sumix = sumix + pix * (i-1)
+ sumix2 = sumix2 + pix * (i-1) ** 2
+ }
+
+ if (fp_equalr (sumi, 0.))
+ call error (1, "zero marginal vector")
+
+ else {
+ centroid = sumix / sumi
+ err = sumix2 / sumi - centroid ** 2
+ if (err > 0.)
+ err = sqrt (err / sumi)
+ else
+ err = 0.
+ }
+
+ return (centroid)
+end
+
+
+# IA_OPENP2R -- Open a list file from which two real values per line
+# are expected.
+
+pointer procedure ia_openp2r (param)
+
+char param[ARB] #I parameter name
+
+int fd, length
+pointer lp, fname, sp
+real x1, x2
+
+int open(), fscan(), nscan(), strmatch()
+
+errchk open
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+
+ call clgstr (param, Memc[fname], SZ_FNAME)
+
+ # Whitespace in the name ?
+ if (strmatch (Memc[fname], "^#$") != 0) {
+ call sfree (sp)
+ return (NULL)
+ }
+
+ # This should be replaced by some template mechanism.
+ ifnoerr (fd = open (Memc[fname], READ_ONLY, TEXT_FILE)) {
+ length = 0
+ while (fscan (fd) != EOF) {
+ call gargr (x1)
+ call gargr (x2)
+
+ switch (nscan()) {
+ case 2:
+ length = length + 1
+ case 1:
+ call error (1, "Reading file, only one value on line")
+ default:
+ # read another line
+ }
+ }
+ call seek (fd, BOF)
+ } else {
+ fd = NULL
+ length = 0
+ }
+
+ call sfree (sp)
+
+ call malloc (lp, LEN_LP, TY_STRUCT)
+ LP_FD(lp) = fd
+ LP_LEN(lp) = length
+
+ return (lp)
+end
+
+
+# IA_LEN -- Return the length of a list file, given its descriptor.
+
+int procedure ia_len (lp)
+
+pointer lp #I list file descriptor
+
+begin
+ if (lp == NULL)
+ return (0)
+ else
+ return (LP_LEN(lp))
+end
+
+
+# IA_GET2R -- Get two real numbers from the next line of the list file.
+
+int procedure ia_get2r (lp, x1, x2)
+
+pointer lp #I list file descriptor
+real x1, x2 #O values to read
+
+int fscan(), nscan()
+
+begin
+ if (lp == NULL) {
+ x1 = INDEFR
+ x2 = INDEFR
+ return (EOF)
+ }
+
+ while (fscan (LP_FD(lp)) != EOF) {
+ call gargr (x1)
+ call gargr (x2)
+
+ switch (nscan()) {
+ case 2:
+ return (2)
+ case 1:
+ call error (1, "only one value on line")
+ default:
+ # read another line
+ }
+ }
+
+ x1 = INDEFR
+ x2 = INDEFR
+ return (EOF)
+end
+
+
+# IA_CLOSE -- Close a list file descriptor.
+
+procedure ia_close (lp)
+
+pointer lp #I list file descriptor
+
+errchk close
+
+begin
+ if (lp == NULL)
+ return
+
+ if (LP_FD(lp) != NULL)
+ call close (LP_FD(lp))
+
+ call mfree (lp, TY_STRUCT)
+ lp = NULL # just in case...
+end
+
+
+# IA_STATS -- Compute the x and y shifts.
+
+procedure ia_stats (cp, imlist)
+
+pointer cp #I center structure pointer
+pointer imlist #I image template (for labeling)
+
+real xshift, yshift, xsum, ysum
+real xsum2, ysum2, xsig2, ysig2
+real xvar, yvar, xerr, yerr, xprop, yprop
+int nim, ncoo, nsources, i, j
+pointer img, sp
+bool firsttime
+
+int imtgetim()
+
+begin
+ call smark (sp)
+ call salloc (img, SZ_FNAME, TY_CHAR)
+
+ nim = NIMAGES(cp)
+ ncoo = NCOORDS(cp)
+
+ firsttime = true
+ for (i=1; imtgetim (imlist, Memc[img], SZ_FNAME) != EOF; i=i+1) {
+ xsum = 0.
+ ysum = 0.
+ xsum2 = 0.
+ ysum2 = 0.
+ xsig2 = 0.
+ ysig2 = 0.
+ nsources = 0
+
+ do j = 1, ncoo {
+ if (REJECTED(cp,i,j) == YES || REJECTED(cp,nim+1,j) == YES)
+ next
+
+ xshift = XCENTER(cp,nim+1,j) - XCENTER(cp,i,j)
+ yshift = YCENTER(cp,nim+1,j) - YCENTER(cp,i,j)
+
+ xsum = xsum + xshift
+ ysum = ysum + yshift
+
+ # internal errors
+ xsum2 = xsum2 + xshift*xshift
+ ysum2 = ysum2 + yshift*yshift
+
+ xsig2 = xsig2 + XSIGMA(cp,nim+1,j)**2 + XSIGMA(cp,i,j)**2
+ ysig2 = ysig2 + YSIGMA(cp,nim+1,j)**2 + YSIGMA(cp,i,j)**2
+
+ nsources = nsources + 1
+ }
+
+ if (nsources == 0) {
+ XSHIFT(cp,i) = INDEFR
+ YSHIFT(cp,i) = INDEFR
+ next
+ }
+
+ XSHIFT(cp,i) = xsum / nsources
+ YSHIFT(cp,i) = ysum / nsources
+
+ if (nsources > 1) {
+ xvar = (nsources*xsum2 - xsum*xsum) / (nsources * (nsources-1))
+ yvar = (nsources*ysum2 - ysum*ysum) / (nsources * (nsources-1))
+ xerr = sqrt (max (xvar/nsources, 0.))
+ yerr = sqrt (max (yvar/nsources, 0.))
+ } else {
+ xerr = INDEFR
+ yerr = INDEFR
+ }
+
+ xprop = sqrt (max (xsig2, 0.)) / nsources
+ yprop = sqrt (max (ysig2, 0.)) / nsources
+
+ if (firsttime) {
+ call printf ("#Shifts%16tImage X-shift Err ")
+ call printf ("Y-shift Err N Internal\n")
+ firsttime = false
+ }
+
+ call printf (
+ "%20s %8.3f (%.3f) %8.3f (%.3f) %4d (%.3f,%.3f)\n")
+ call pargstr (Memc[img])
+ call pargr (XSHIFT(cp,i))
+ call pargr (xprop)
+ call pargr (YSHIFT(cp,i))
+ call pargr (yprop)
+ call pargi (nsources)
+ call pargr (xerr)
+ call pargr (yerr)
+ }
+
+ call flush (STDOUT)
+ call sfree (sp)
+end
+
+
+# IA_TRIM -- Compute the trim section.
+
+procedure ia_trim (cp)
+
+pointer cp #I center structure pointer
+
+real xlo, xhi, ylo, yhi, xmin, ymin
+int ixlo, ixhi, iylo, iyhi, ixlonew, ixhinew, iylonew, iyhinew, i
+int vxlo, vxhi, vylo, vyhi # vignetted versions
+bool firsttime
+
+begin
+ firsttime = true
+ do i = 1, NIMAGES(cp) {
+
+ if (IS_INDEFR(XSHIFT(cp,i)) || IS_INDEFR(YSHIFT(cp,i)))
+ next
+
+ # Compute limits.
+ xlo = 1. + XSHIFT(cp,i)
+ ylo = 1. + YSHIFT(cp,i)
+ xhi = XSIZE(cp,i) + XSHIFT(cp,i)
+ yhi = YSIZE(cp,i) + YSHIFT(cp,i)
+
+ ixlonew = int (xlo)
+ if (xlo > ixlonew) # round up
+ ixlonew = ixlonew + 1
+
+ ixhinew = int (xhi)
+ if (xhi < ixhinew) # round down
+ ixhinew = ixhinew - 1
+
+ iylonew = int (ylo) # round up
+ if (ylo > iylonew)
+ iylonew = iylonew + 1
+
+ iyhinew = int (yhi) # round down
+ if (yhi < iyhinew)
+ iyhinew = iyhinew - 1
+
+ if (firsttime) {
+ ixlo = ixlonew
+ ixhi = ixhinew
+ iylo = iylonew
+ iyhi = iyhinew
+
+ xmin = XSIZE(cp,i)
+ ymin = YSIZE(cp,i)
+
+ firsttime = false
+ } else {
+ ixlo = max (ixlo, ixlonew)
+ ixhi = min (ixhi, ixhinew)
+ iylo = max (iylo, iylonew)
+ iyhi = min (iyhi, iyhinew)
+
+ xmin = min (XSIZE(cp,i), xmin)
+ ymin = min (YSIZE(cp,i), ymin)
+ }
+ }
+
+ # Don't bother to complain.
+ if (firsttime)
+ return
+
+ call printf ("\n")
+
+ # Vignetting is possible downstream since imshift and other tasks
+ # preserve the size of the input image.
+
+ vxlo = max (1, min (ixlo, int(xmin)))
+ vxhi = max (1, min (ixhi, int(xmin)))
+ vylo = max (1, min (iylo, int(ymin)))
+ vyhi = max (1, min (iyhi, int(ymin)))
+ if (vxlo != ixlo || vxhi != ixhi || vylo != iylo || vyhi != iyhi) {
+ call eprintf ("#Vignette_Section = [%d:%d,%d:%d]\n")
+ call pargi (vxlo)
+ call pargi (vxhi)
+ call pargi (vylo)
+ call pargi (vyhi)
+ }
+
+ # Output the trim section.
+ call printf ("#Trim_Section = [%d:%d,%d:%d]\n")
+ call pargi (ixlo)
+ call pargi (ixhi)
+ call pargi (iylo)
+ call pargi (iyhi)
+
+ call flush (STDOUT)
+end
diff --git a/pkg/images/immatch/src/listmatch/t_xyxymatch.x b/pkg/images/immatch/src/listmatch/t_xyxymatch.x
new file mode 100644
index 00000000..1c8a16c5
--- /dev/null
+++ b/pkg/images/immatch/src/listmatch/t_xyxymatch.x
@@ -0,0 +1,406 @@
+include <fset.h>
+include "../../../lib/xyxymatch.h"
+
+# T_XYXYMATCH -- This task computes the intersection of a set of
+# of coordinate lists with a reference coordinate list. The output is
+# the set of objects common to both lists. In its simplest form LINXYMATCH
+# uses a matching tolerance to generate the common list. Alternatively
+# XYXYMATCH can use coordinate transformation information derived from the
+# positions of one to three stars common to both lists, a sorting algorithm,
+# and a matching tolerance to generate the common list. A more sophisticated
+# pattern matching algorithm is also available which requires no coordinate
+# transformation input from the user but is expensive computationally.
+
+procedure t_xyxymatch()
+
+bool interactive, verbose
+int ilist, rlist, olist, rfd, rpfd, ifd, ofd
+int xcol, ycol, xrefcol, yrefcol, maxntriangles, nreftie, nintie
+int ntie, match, nrefstars, nliststars, ninter, nrmaxtri, nreftri
+int ninmaxtri, nintri, ntrefstars, ntliststars, nreject
+pointer sp, inname, refname, outname, refpoints, str, xreftie, yreftie
+pointer xintie, yintie, coeff, xref, yref, rlineno, rsindex, reftri, reftrirat
+pointer xlist, ylist, listindex, ilineno, xtrans, ytrans, intri, intrirat
+pointer xformat, yformat
+real tolerance, separation, xin, yin, xmag, ymag, xrot, yrot, xout, yout
+real ratio
+
+bool clgetb()
+int clpopnu(), clplen(), clgeti(), clgfil(), open(), clgwrd()
+int rg_getreftie(), rg_lincoeff(), fstati(), rg_rdxyi(), rg_sort()
+int rg_intersection(), rg_factorial(), rg_triangle(), rg_match()
+int rg_mlincoeff()
+real clgetr()
+
+begin
+ if (fstati (STDOUT, F_REDIR) == NO)
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (inname, SZ_FNAME, TY_CHAR)
+ call salloc (refname, SZ_FNAME, TY_CHAR)
+ call salloc (outname, SZ_FNAME, TY_CHAR)
+ call salloc (refpoints, SZ_FNAME, TY_CHAR)
+ call salloc (xreftie, MAX_NTIE, TY_REAL)
+ call salloc (yreftie, MAX_NTIE, TY_REAL)
+ call salloc (xintie, MAX_NTIE, TY_REAL)
+ call salloc (yintie, MAX_NTIE, TY_REAL)
+ call salloc (coeff, MAX_NCOEFF, TY_REAL)
+ call salloc (xformat, SZ_FNAME, TY_CHAR)
+ call salloc (yformat, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Get the input, output, and reference lists.
+ ilist = clpopnu ("input")
+ rlist = clpopnu ("reference")
+ olist = clpopnu ("output")
+ tolerance = clgetr ("tolerance")
+ call clgstr ("refpoints", Memc[refpoints], SZ_FNAME)
+
+ # Check the input and output file lengths.
+ if (clplen (rlist) > 1 && clplen (rlist) != clplen (ilist))
+ call error (0,
+ "The number of input and reference lists are not the same")
+ if (clplen (ilist) != clplen (olist))
+ call error (0,
+ "The number of input and output lists are not the same")
+
+ xcol = clgeti ("xcolumn")
+ ycol = clgeti ("ycolumn")
+ xrefcol = clgeti ("xrcolumn")
+ yrefcol = clgeti ("yrcolumn")
+
+ # Get the matching parameters.
+ match = clgwrd ("matching", Memc[str], SZ_LINE, RG_MATCHSTR)
+ xin = clgetr ("xin")
+ if (IS_INDEFR(xin))
+ xin = 0.0
+ yin = clgetr ("yin")
+ if (IS_INDEFR(yin))
+ yin = 0.0
+ xmag = clgetr ("xmag")
+ if (IS_INDEFR(xmag))
+ xmag = 1.0
+ ymag = clgetr ("ymag")
+ if (IS_INDEFR(ymag))
+ ymag = 1.0
+ xrot = clgetr ("xrotation")
+ if (IS_INDEFR(xrot))
+ xrot = 0.0
+ yrot = clgetr ("yrotation")
+ if (IS_INDEFR(yrot))
+ yrot = 0.0
+ xout = clgetr ("xref")
+ if (IS_INDEFR(xout))
+ xout = 0.0
+ yout = clgetr ("yref")
+ if (IS_INDEFR(yout))
+ yout = 0.0
+
+ # Get the algorithm parameters.
+ separation = clgetr ("separation")
+ maxntriangles = clgeti ("nmatch")
+ ratio = clgetr ("ratio")
+ nreject = clgeti ("nreject")
+
+ # Get the output formatting parameters.
+ call clgstr ("xformat", Memc[xformat], SZ_FNAME)
+ call clgstr ("yformat", Memc[yformat], SZ_FNAME)
+
+ interactive = clgetb ("interactive")
+ verbose = clgetb ("verbose")
+
+ # Open the reference list file if any.
+ rfd = NULL
+ if (Memc[refpoints] == EOS)
+ rpfd = NULL
+ else
+ rpfd = open (Memc[refpoints], READ_ONLY, TEXT_FILE)
+
+ # Initialize.
+ xref = NULL
+ yref = NULL
+ rsindex = NULL
+ rlineno = NULL
+
+ # Loop over the input lists.
+ while (clgfil (ilist, Memc[inname], SZ_FNAME) != EOF &&
+ clgfil (olist, Memc[outname], SZ_FNAME) != EOF) {
+
+ # Open the input list.
+ ifd = open (Memc[inname], READ_ONLY, TEXT_FILE)
+
+ # Open the output list.
+ ofd = open (Memc[outname], NEW_FILE, TEXT_FILE)
+
+ # Open the reference list and get the coordinates.
+ while (clgfil (rlist, Memc[refname], SZ_FNAME) != EOF) {
+
+ # Open the reference file.
+ if (rfd != NULL)
+ call close (rfd)
+ rfd = open (Memc[refname], READ_ONLY, TEXT_FILE)
+
+ # Fetch the reference tie points.
+ if (interactive || rpfd != NULL)
+ nreftie = rg_getreftie (rpfd, Memr[xreftie],
+ Memr[yreftie], 3, RG_REFFILE, interactive)
+ else
+ nreftie = 0
+
+ # Read the reference data.
+ if (xref != NULL)
+ call mfree (xref, TY_REAL)
+ if (yref != NULL)
+ call mfree (yref, TY_REAL)
+ if (rlineno != NULL)
+ call mfree (rlineno, TY_INT)
+ if (rsindex != NULL)
+ call mfree (rsindex, TY_INT)
+ ntrefstars = rg_rdxyi (rfd, xref, yref, rlineno, xrefcol,
+ yrefcol)
+ call malloc (rsindex, ntrefstars, TY_INT)
+
+ # Prepare the reference list for the merge algorithm. If a tie
+ # point matching algorithm is selected, sort the list in the
+ # y and then the x coordinate and remove coincident points.
+ # If the pattern matching algorithm is used then construct the
+ # triangles used for matching and sort them in order of
+ # increasing ratio.
+
+ nrefstars = rg_sort (Memr[xref], Memr[yref], Memi[rsindex],
+ ntrefstars, separation, YES, YES)
+ if (match != RG_TRIANGLES) {
+ reftri = NULL
+ reftrirat = NULL
+ nreftri = nrefstars
+ } else if (nrefstars > 2) {
+ nrmaxtri = rg_factorial (min (nrefstars, maxntriangles), 3)
+ call calloc (reftri, SZ_TRIINDEX * nrmaxtri, TY_INT)
+ call calloc (reftrirat, SZ_TRIPAR * nrmaxtri, TY_REAL)
+ nreftri = rg_triangle (Memr[xref], Memr[yref],
+ Memi[rsindex], nrefstars, Memi[reftri],
+ Memr[reftrirat], nrmaxtri, maxntriangles,
+ tolerance, ratio)
+ } else {
+ nreftri = 0
+ reftri = NULL
+ reftrirat = NULL
+ }
+
+ break
+ }
+
+ # Fetch the input tie points and compute the coefficients.
+ if (interactive || rpfd != NULL)
+ nintie = rg_getreftie (rpfd, Memr[xintie],
+ Memr[yintie], nreftie, RG_INFILE, interactive)
+ else
+ nintie = 0
+ ntie = min (nreftie, nintie)
+ if (ntie <= 0)
+ call rg_lmkcoeff (xin, yin, xmag, ymag, xrot, yrot,
+ xout, yout, Memr[coeff], MAX_NCOEFF)
+ else if (rg_lincoeff (Memr[xreftie], Memr[yreftie],
+ Memr[xintie], Memr[yintie], ntie, Memr[coeff],
+ MAX_NCOEFF) == ERR)
+ call rg_lmkcoeff (xin, yin, xmag, ymag, xrot, yrot,
+ xout, yout, Memr[coeff], MAX_NCOEFF)
+
+ # Print the header.
+ if (verbose) {
+ call printf ("\nInput: %s Reference: %s ")
+ call pargstr (Memc[inname])
+ call pargstr (Memc[refname])
+ call printf ("Number of tie points: %d\n")
+ call pargi (ntie)
+ }
+ call fprintf (ofd, "\n# Input: %s Reference: %s ")
+ call pargstr (Memc[inname])
+ call pargstr (Memc[refname])
+ call fprintf (ofd, "Number of tie points: %d\n")
+ call pargi (ntie)
+
+ # Print the coordinate transformation information.
+ if (verbose)
+ call rg_plincoeff ("xref", "yref", Memr[xreftie],
+ Memr[yreftie], Memr[xintie], Memr[yintie], ntie,
+ Memr[coeff], MAX_NCOEFF)
+ call rg_wlincoeff (ofd, "xref", "yref", Memr[xreftie],
+ Memr[yreftie], Memr[xintie], Memr[yintie], ntie,
+ Memr[coeff], MAX_NCOEFF)
+
+ # Read in the input list.
+ xtrans = NULL
+ ytrans = NULL
+ listindex = NULL
+ ntliststars = rg_rdxyi (ifd, xlist, ylist, ilineno, xcol, ycol)
+
+ # Compute the intersection of the two lists using either an
+ # algorithm depending on common tie points or on a more
+ # sophisticated pattern matching algorithm.
+
+ if (ntrefstars <= 0) {
+ if (verbose)
+ call printf (" The reference coordinate list is empty\n")
+ ninter = 0
+ } else if (ntliststars <= 0) {
+ if (verbose)
+ call printf (" The input coordinate list is empty\n")
+ ninter = 0
+ } else if (nreftri <= 0) {
+ if (verbose)
+ call printf (
+ " No valid reference triangles can be defined\n")
+ } else {
+ call malloc (xtrans, ntliststars, TY_REAL)
+ call malloc (ytrans, ntliststars, TY_REAL)
+ call malloc (listindex, ntliststars, TY_INT)
+ call rg_compute (Memr[xlist], Memr[ylist], Memr[xtrans],
+ Memr[ytrans], ntliststars, Memr[coeff], MAX_NCOEFF)
+ nliststars = rg_sort (Memr[xtrans], Memr[ytrans],
+ Memi[listindex], ntliststars, separation, YES, YES)
+ if (match != RG_TRIANGLES) {
+ intri = NULL
+ intrirat = NULL
+ nintri = nliststars
+ call rg_pxycolumns (ofd)
+ ninter = rg_intersection (ofd, Memr[xref], Memr[yref],
+ Memi[rsindex], Memi[rlineno], nrefstars, Memr[xlist],
+ Memr[ylist], Memr[xtrans], Memr[ytrans],
+ Memi[listindex], Memi[ilineno], nliststars, tolerance,
+ Memc[xformat], Memc[yformat])
+ } else if (nliststars > 2) {
+ ninmaxtri = rg_factorial (min (max(nliststars,nrefstars),
+ maxntriangles), 3)
+ call calloc (intri, SZ_TRIINDEX * ninmaxtri, TY_INT)
+ call calloc (intrirat, SZ_TRIPAR * ninmaxtri, TY_REAL)
+ nintri = rg_triangle (Memr[xtrans], Memr[ytrans],
+ Memi[listindex], nliststars, Memi[intri],
+ Memr[intrirat], ninmaxtri, maxntriangles,
+ tolerance, ratio)
+ if (nintri <= 0) {
+ if (verbose)
+ call printf (
+ " No valid input triangles can be defined\n")
+ } else {
+ ninter = rg_match (Memr[xref], Memr[yref], nrefstars,
+ Memr[xtrans], Memr[ytrans], nliststars,
+ Memi[reftri], Memr[reftrirat], nreftri, nrmaxtri,
+ ntrefstars, Memi[intri], Memr[intrirat], nintri,
+ ninmaxtri, ntliststars, tolerance, tolerance,
+ ratio, nreject)
+ }
+ if (nrefstars <= maxntriangles && nliststars <=
+ maxntriangles) {
+ call rg_pxycolumns (ofd)
+ call rg_mwrite (ofd, Memr[xref], Memr[yref],
+ Memi[rlineno], Memr[xlist], Memr[ylist],
+ Memi[ilineno], Memi[reftri], nrmaxtri,
+ Memi[intri], ninmaxtri, ninter, Memc[xformat],
+ Memc[yformat])
+ } else {
+ if (rg_mlincoeff (Memr[xref], Memr[yref], Memr[xlist],
+ Memr[ylist], Memi[reftri], nrmaxtri,
+ Memi[intri], ninmaxtri, ninter, Memr[coeff],
+ MAX_NCOEFF) == ERR)
+ call rg_lmkcoeff (xin, yin, xmag, ymag, xrot, yrot,
+ xout, yout, Memr[coeff], MAX_NCOEFF)
+ call rg_compute (Memr[xlist], Memr[ylist],
+ Memr[xtrans], Memr[ytrans], ntliststars,
+ Memr[coeff], MAX_NCOEFF)
+ nliststars = rg_sort (Memr[xtrans], Memr[ytrans],
+ Memi[listindex], ntliststars, separation,
+ YES, YES)
+ if (verbose)
+ call rg_pmlincoeff ("xref", "yref", Memr[coeff],
+ MAX_NCOEFF)
+ call rg_wmlincoeff (ofd, "xref", "yref", Memr[coeff],
+ MAX_NCOEFF)
+ call rg_pxycolumns (ofd)
+ ninter = rg_intersection (ofd, Memr[xref], Memr[yref],
+ Memi[rsindex], Memi[rlineno], nrefstars,
+ Memr[xlist], Memr[ylist], Memr[xtrans],
+ Memr[ytrans], Memi[listindex], Memi[ilineno],
+ nliststars, tolerance, Memc[xformat], Memc[yformat])
+ }
+ } else {
+ if (verbose)
+ call printf (
+ "\tThe input coordinate list has < 3 stars\n")
+ intri = NULL
+ intrirat = NULL
+ nintri = 0
+ ninter = 0
+ }
+ }
+
+ # Print out the number of stars matched in the two lists.
+ if (verbose) {
+ call printf ("%d reference coordinates matched\n")
+ call pargi (ninter)
+ }
+
+ # Free space used by input list.
+ call mfree (xlist, TY_REAL)
+ call mfree (ylist, TY_REAL)
+ call mfree (ilineno, TY_INT)
+ call mfree (listindex, TY_INT)
+ if (xtrans != NULL)
+ call mfree (xtrans, TY_REAL)
+ if (ytrans != NULL)
+ call mfree (ytrans, TY_REAL)
+ if (intri != NULL)
+ call mfree (intri, TY_INT)
+ if (intrirat != NULL)
+ call mfree (intrirat, TY_REAL)
+
+ # Close the input and output lists.
+ call close (ifd)
+ call close (ofd)
+ }
+
+ # Release the memory used to store the reference list.
+ call mfree (xref, TY_REAL)
+ call mfree (yref, TY_REAL)
+ call mfree (rlineno, TY_INT)
+ call mfree (rsindex, TY_INT)
+ if (reftri != NULL)
+ call mfree (reftri, TY_INT)
+ if (reftrirat != NULL)
+ call mfree (reftrirat, TY_REAL)
+
+ # Close the reference file.
+ if (rfd != NULL)
+ call close (rfd)
+
+ # Close the reference points file.
+ if (rpfd != NULL)
+ call close (rpfd)
+
+ # Close the file lists.
+ call clpcls (ilist)
+ call clpcls (rlist)
+ call clpcls (olist)
+
+ call sfree (sp)
+end
+
+
+# RG_PXYCOLUMNS -- Print the column descriptions in the output file.
+
+procedure rg_pxycolumns (ofd)
+
+int ofd #I the output file descriptor
+
+begin
+ call fprintf (ofd, "# Column definitions\n")
+ call fprintf (ofd, "# Column 1: X reference coordinate\n")
+ call fprintf (ofd, "# Column 2: Y reference coordinate\n")
+ call fprintf (ofd, "# Column 3: X input coordinate\n")
+ call fprintf (ofd, "# Column 4: Y input coordinate\n")
+ call fprintf (ofd, "# Column 5: Reference line number\n")
+ call fprintf (ofd, "# Column 6: Input line number\n")
+ call fprintf (ofd, "\n")
+end
diff --git a/pkg/images/immatch/src/mkpkg b/pkg/images/immatch/src/mkpkg
new file mode 100644
index 00000000..ec8accec
--- /dev/null
+++ b/pkg/images/immatch/src/mkpkg
@@ -0,0 +1,11 @@
+# Library for the IMMATCH Package.
+
+libpkg.a:
+ @geometry
+ @imcombine
+ @linmatch
+ @listmatch
+ @psfmatch
+ @wcsmatch
+ @xregister
+ ;
diff --git a/pkg/images/immatch/src/psfmatch/mkpkg b/pkg/images/immatch/src/psfmatch/mkpkg
new file mode 100644
index 00000000..da3951dc
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/mkpkg
@@ -0,0 +1,21 @@
+# Make the PSFMATCH task
+
+$checkout libpkg.a ../../../
+$update libpkg.a
+$checkin libpkg.a ../../../
+$exit
+
+libpkg.a:
+ rgpbckgrd.x <math.h> <math/gsurfit.h> "psfmatch.h"
+ rgpcolon.x <imhdr.h> <imset.h> <error.h> "psfmatch.h"
+ rgpconvolve.x <error.h> <imhdr.h> <imset.h>
+ rgpisfm.x <imhdr.h> <gset.h> <ctype.h> "psfmatch.h"
+ rgpfft.x
+ rgpfilter.x <math.h>
+ rgppars.x "psfmatch.h"
+ rgpregions.x <imhdr.h> <fset.h> "psfmatch.h"
+ rgpsfm.x <imhdr.h> <math/gsurfit.h> "psfmatch.h"
+ rgpshow.x "psfmatch.h"
+ rgptools.x "psfmatch.h"
+ t_psfmatch.x <fset.h> <imhdr.h> "psfmatch.h"
+ ;
diff --git a/pkg/images/immatch/src/psfmatch/psfmatch.h b/pkg/images/immatch/src/psfmatch/psfmatch.h
new file mode 100644
index 00000000..c6b7d563
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/psfmatch.h
@@ -0,0 +1,274 @@
+# Header file for PSFMATCH
+
+define LEN_PSFSTRUCT (45 + 12 * SZ_FNAME + 12)
+
+# Define the psf fitting structure
+
+define PM_RC1 Memi[$1] # pointer to first column of region
+define PM_RC2 Memi[$1+1] # pointer to last column of region
+define PM_RL1 Memi[$1+2] # pointer to first line of region
+define PM_RL2 Memi[$1+3] # pointer to last line of region
+define PM_RZERO Memi[$1+4] # pointer to zero point of ref regions
+define PM_RXSLOPE Memi[$1+5] # pointer to x slopes of ref regions
+define PM_RYSLOPE Memi[$1+6] # pointer to y slopes of ref regions
+define PM_NREGIONS Memi[$1+7] # total number of regions
+define PM_CNREGION Memi[$1+8] # the current region
+
+define PM_CENTER Memi[$1+9] # the the psf objects
+define PM_BACKGRD Memi[$1+10] # type of background subtraction
+define PM_BVALUER Memr[P2R($1+11)] # reference background value
+define PM_BVALUE Memr[P2R($1+12)] # image background value
+define PM_LOREJECT Memr[P2R($1+13)] # low side rejection
+define PM_HIREJECT Memr[P2R($1+14)] # high side rejection
+define PM_APODIZE Memr[P2R($1+15)] # fraction of region to be apodized
+
+define PM_CONVOLUTION Memi[$1+16] # the convolution type
+define PM_DNX Memi[$1+17] # x dimension of kernel
+define PM_DNY Memi[$1+18] # y dimension of kernel
+define PM_PNX Memi[$1+19] # x dimension of user kernel
+define PM_PNY Memi[$1+20] # y dimension of user kernel
+define PM_KNX Memi[$1+21] # x size of kernel
+define PM_KNY Memi[$1+22] # x size of kernel
+
+define PM_POWER Memi[$1+23] # save power spectrum of kernel ?
+
+define PM_UFLUXRATIO Memr[P2R($1+24)] # the user ref / input flux ratio
+define PM_FLUXRATIO Memr[P2R($1+25)] # ref / input flux ratio
+define PM_FILTER Memi[$1+26] # background filtering
+define PM_SXINNER Memr[P2R($1+27)] # inner radius for cosine bell
+define PM_SXOUTER Memr[P2R($1+28)] # outer radius for cosine bell
+define PM_SYINNER Memr[P2R($1+29)] # inner radius for cosine bell
+define PM_SYOUTER Memr[P2R($1+30)] # outer radius for cosine bell
+define PM_RADSYM Memi[$1+31] # radial symmetry in convolution
+define PM_THRESHOLD Memr[P2R($1+32)] # threshold in divisor for model
+
+define PM_NORMFACTOR Memr[P2R($1+34)] # the normalization factor
+
+#define PM_PRATIO Memr[P2R($1+24)] # power ration threshold
+#define PM_XSHIFTS Memi[$1+26] # pointer to x shifts
+#define PM_YSHIFTS Memi[$1+27] # pointer to y shifts
+
+define PM_REFFFT Memi[$1+35] # pointer to reference fft
+define PM_IMFFT Memi[$1+36] # pointer to image fft
+define PM_FFT Memi[$1+37] # pointer to unfiltered fft
+define PM_CONV Memi[$1+38] # pointer to kernel
+define PM_ASFFT Memi[$1+39] # pointer to power spectrum
+define PM_NXFFT Memi[$1+40] # x dimension of FFT
+define PM_NYFFT Memi[$1+41] # y dimension of FFT
+
+define PM_BSTRING Memc[P2C($1+42)] # background string
+define PM_CSTRING Memc[P2C($1+42+SZ_FNAME+1)] # convolution string
+define PM_FSTRING Memc[P2C($1+42+2*SZ_FNAME+2)] # convolution string
+
+define PM_IMAGE Memc[P2C($1+42+4*SZ_FNAME+4)] # input image
+define PM_REFIMAGE Memc[P2C($1+42+5*SZ_FNAME+5)] # reference image
+define PM_PSFDATA Memc[P2C($1+42+6*SZ_FNAME+6)] # psf data
+define PM_PSFIMAGE Memc[P2C($1+42+7*SZ_FNAME+7)] # psf image if any
+define PM_OBJLIST Memc[P2C($1+42+8*SZ_FNAME+8)] # object list if any
+define PM_KERNEL Memc[P2C($1+42+9*SZ_FNAME+9)] # kernel image
+define PM_OUTIMAGE Memc[P2C($1+42+10*SZ_FNAME+10)] # output convolved image
+
+# Define the paramerter ids
+
+define RC1 1
+define RC2 2
+define RL1 3
+define RL2 4
+define RZERO 5
+define RXSLOPE 6
+define RYSLOPE 7
+define NREGIONS 8
+define CNREGION 9
+
+define CENTER 10
+define BACKGRD 11
+define BVALUER 12
+define BVALUE 13
+define LOREJECT 15
+define HIREJECT 16
+define APODIZE 17
+
+define CONVOLUTION 18
+define DNX 19
+define DNY 20
+define PNX 21
+define PNY 22
+define KNX 23
+define KNY 24
+define POWER 25
+
+#define XSHIFTS 20
+#define YSHIFTS 21
+
+define REFFFT 26
+define IMFFT 27
+define FFT 28
+define CONV 29
+define ASFFT 30
+define NXFFT 31
+define NYFFT 32
+
+define UFLUXRATIO 33
+define FLUXRATIO 34
+define FILTER 35
+define SXINNER 36
+define SXOUTER 37
+define SYINNER 38
+define SYOUTER 39
+define RADSYM 40
+define THRESHOLD 41
+
+define NORMFACTOR 43
+
+#define PRATIO 34
+
+define BSTRING 44
+define CSTRING 45
+define FSTRING 46
+
+define REFIMAGE 48
+define IMAGE 49
+define PSFDATA 50
+define PSFIMAGE 51
+define OBJLIST 52
+define KERNEL 53
+define OUTIMAGE 54
+
+# Define the default parameter values
+
+define DEF_CENTER YES
+define DEF_BACKGRD PM_BMEDIAN
+define DEF_LOREJECT INDEFR
+define DEF_HIREJECT INDEFR
+
+define DEF_CONVOLUTION PM_CONIMAGE
+define DEF_DNX 63
+define DEF_DNY 63
+define DEF_PNX 31
+define DEF_PNY 31
+define DEF_POWER NO
+
+define DEF_FILTER PM_FREPLACE
+define DEF_SXINNER INDEFR
+define DEF_SXOUTER INDEFR
+define DEF_SYINNER INDEFR
+define DEF_SYOUTER INDEFR
+define DEF_RADSYM NO
+define DEF_THRESHOLD 0.0
+
+#define DEF_PRATIO 0.0
+
+define DEF_NORMFACTOR 1.0
+define DEF_UFLUXRATIO INDEFR
+
+# Define the background fitting techniques
+
+define PM_BNONE 1
+define PM_BMEAN 2
+define PM_BMEDIAN 3
+define PM_BSLOPE 4
+define PM_BNUMBER 5
+
+define PM_BTYPES "|none|mean|median|plane|"
+
+# Define the convolution computation options
+
+define PM_CONIMAGE 1
+define PM_CONPSF 2
+define PM_CONKERNEL 3
+
+define PM_CTYPES "|image|psf|kernel|"
+
+# Define the filtering options
+
+define PM_FNONE 1
+define PM_FCOSBELL 2
+define PM_FREPLACE 3
+define PM_FMODEL 4
+
+define PM_FTYPES "|none|cosbell|replace|model|"
+
+# Define the normalization options
+
+define PM_UNIT 1
+define PM_RATIO 2
+define PM_NUMBER 3
+
+define PM_NTYPES "|unit|ratio|"
+
+# Miscellaneous
+
+define MAX_NREGIONS 100
+
+# Commands
+
+define PMCMDS "|input|reference|psfdata|psfimage|kernel|output|dnx|dny|\
+pnx|pny|center|background|loreject|hireject|apodize|convolution|fluxratio|\
+filter|sx1|sx2|sy1|sy2|radsym|threshold|normfactor|show|mark|"
+
+define PMCMD_IMAGE 1
+define PMCMD_REFIMAGE 2
+define PMCMD_PSFDATA 3
+define PMCMD_PSFIMAGE 4
+define PMCMD_KERNEL 5
+define PMCMD_OUTIMAGE 6
+
+define PMCMD_DNX 7
+define PMCMD_DNY 8
+define PMCMD_PNX 9
+define PMCMD_PNY 10
+
+define PMCMD_CENTER 11
+define PMCMD_BACKGRD 12
+define PMCMD_LOREJECT 13
+define PMCMD_HIREJECT 14
+define PMCMD_APODIZE 15
+
+define PMCMD_CONVOLUTION 16
+define PMCMD_UFLUXRATIO 17
+define PMCMD_FILTER 18
+define PMCMD_SXINNER 19
+define PMCMD_SXOUTER 20
+define PMCMD_SYINNER 21
+define PMCMD_SYOUTER 22
+define PMCMD_RADSYM 23
+define PMCMD_THRESHOLD 24
+
+define PMCMD_NORMFACTOR 25
+
+define PMCMD_SHOW 26
+define PMCMD_MARK 27
+
+# Keywords
+
+define KY_IMAGE "input"
+define KY_REFIMAGE "reference"
+define KY_PSFDATA "psfdata"
+define KY_PSFIMAGE "psfimage"
+define KY_KERNEL "kernel"
+define KY_OUTIMAGE "output"
+
+define KY_DNX "dnx"
+define KY_DNY "dny"
+define KY_PNX "pnx"
+define KY_PNY "pny"
+
+define KY_CENTER "center"
+define KY_BACKGRD "background"
+define KY_LOREJECT "loreject"
+define KY_HIREJECT "hireject"
+define KY_APODIZE "apodize"
+
+define KY_CONVOLUTION "convolution"
+
+define KY_UFLUXRATIO "fluxratio"
+define KY_FILTER "filter"
+define KY_SXINNER "sx1"
+define KY_SXOUTER "sx2"
+define KY_SYINNER "sy1"
+define KY_SYOUTER "sy2"
+define KY_RADSYM "radsym"
+define KY_THRESHOLD "threshold"
+
+define KY_NORMFACTOR "normfactor"
+
diff --git a/pkg/images/immatch/src/psfmatch/psfmatch.key b/pkg/images/immatch/src/psfmatch/psfmatch.key
new file mode 100644
index 00000000..57ef3b2e
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/psfmatch.key
@@ -0,0 +1,50 @@
+ Interactive Keystroke Commands
+
+
+? Print help
+: Colon commands
+k Draw a contour plot of the psf matching kernel
+p Draw a contour plot of the psf matching kernel power spectrum
+x Draw a column plot of the psf matching kernel / power spectrum
+y Draw a line plot of the psf matching kernel / power spectrum
+r Redraw the current plot
+f Recompute the psf matching kernel
+w Update the task parameters
+q Exit
+
+
+ Colon Commands
+
+
+:mark [file] Mark objects on the display
+:show Show current values of the parameters
+
+
+ Show/Set Parameters
+
+:input [string] Show/set the current input image name
+:reference [string] Show/set the current reference image/psf name
+:psf [file/string] Show/set the objects/input psf list
+:psfimage [string] Show/set the current input psf name
+:kernel [string] Show/set the current psf matching kernel name
+:output [string] Show/set the current output image name
+
+:dnx [value] Show/set x width of data region(s) to extract
+:dny [value] Show/set y width of data region(s) to extract
+:pnx [value] Show/set x width of psf matching kernel
+:pny [value] Show/set y width of psf matching kernel
+:center [yes/no] Show/set the centering switch
+:background [string] Show/set the background fitting function
+:loreject [value] Show/set low side k-sigma rejection parameter
+:hireject [value] Show/set high side k-sigma rejection parameter
+:apodize [value] Show/set percent of endpoints to apodize
+
+:filter [string] Show/set the filtering algorithm
+:fluxratio [value] Show/set the reference/input psf flux ratio
+:sx1 [value] Show/set inner x frequency for cosbell filter
+:sx2 [value] Show/set outer x frequency for cosbell filter
+:sy1 [value] Show/set inner y frequency for cosbell filter
+:sy2 [value] Show/set outer y frequency for cosbell filter
+:radsym [yes/no] Show/set radial symmetry for cosbell filter
+:threshold [value] Show/set %threshold for replace/modeling filter
+:normfactor [value] Show/set the kernel normalization factor
diff --git a/pkg/images/immatch/src/psfmatch/rgpbckgrd.x b/pkg/images/immatch/src/psfmatch/rgpbckgrd.x
new file mode 100644
index 00000000..1670b943
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpbckgrd.x
@@ -0,0 +1,70 @@
+include <math.h>
+include <math/gsurfit.h>
+include "psfmatch.h"
+
+# RG_PSCALE -- Compute the background offset and x and y slope.
+
+procedure rg_pscale (pm, data, npts, nx, ny, pnx, pny, offset, coeff)
+
+pointer pm #I pointer to the psfmatch structure
+real data[ARB] #I the input data
+int npts #I the number of points
+int nx, ny #I the dimensions of the original subraster
+int pnx, pny #I the dimensions of the data region
+real offset #I the input offset
+real coeff[ARB] #O the output coefficients
+
+int wxborder, wyborder
+pointer gs
+real loreject, hireject, zero
+int rg_pstati(), rg_znsum(), rg_znmedian(), rg_slope()
+real rg_pstatr()
+
+begin
+ loreject = rg_pstatr (pm, LOREJECT)
+ hireject = rg_pstatr (pm, HIREJECT)
+
+ switch (rg_pstati (pm, BACKGRD)) {
+ case PM_BNONE:
+ coeff[1] = 0.0
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ case PM_BNUMBER:
+ coeff[1] = offset
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ case PM_BMEAN:
+ if (rg_znsum (data, npts, zero, loreject, hireject) <= 0)
+ zero = 0.0
+ coeff[1] = zero
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ case PM_BMEDIAN:
+ if (rg_znmedian (data, npts, zero, loreject, hireject) <= 0)
+ zero = 0.0
+ coeff[1] = zero
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ case PM_BSLOPE:
+ call gsinit (gs, GS_POLYNOMIAL, 2, 2, GS_XNONE, 1.0, real (nx), 1.0,
+ real (ny))
+ wxborder = (nx - pnx) / 2
+ wyborder = (ny - pny) / 2
+ if (rg_slope (gs, data, npts, nx, ny, wxborder, wyborder, loreject,
+ hireject) == ERR) {
+ coeff[1] = 0.0
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ } else {
+ call gssave (gs, coeff)
+ coeff[1] = coeff[GS_SAVECOEFF+1]
+ coeff[2] = coeff[GS_SAVECOEFF+2]
+ coeff[3] = coeff[GS_SAVECOEFF+3]
+ }
+ call gsfree (gs)
+ default:
+ coeff[1] = 0.0
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ }
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgpcolon.x b/pkg/images/immatch/src/psfmatch/rgpcolon.x
new file mode 100644
index 00000000..8eefb22d
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpcolon.x
@@ -0,0 +1,501 @@
+include <imhdr.h>
+include <imset.h>
+include <error.h>
+include "psfmatch.h"
+
+# RG_PCOLON -- Show/set the psfmatch task algorithm parameters.
+
+procedure rg_pcolon (gd, pm, imr, reglist, impsf, im1, imk, imfourier, im2,
+ cmdstr, newref, newdata, newfourier, newfilter)
+
+pointer gd #I pointer to the graphics stream
+pointer pm #I pointer to psfmatch structure
+pointer imr #I pointer to the reference image
+int reglist #I the regions / psf list descriptor
+pointer impsf #I pointer to the regions list
+pointer im1 #I pointer to the input image
+pointer imk #I pointer to kernel image
+pointer imfourier #I pointer to fourier spectrum image
+pointer im2 #I pointer to the output image
+char cmdstr[ARB] #I command string
+int newref #I/O new reference image
+int newdata #I/O new input image
+int newfourier #I/O new FFT
+int newfilter #I/O new filter
+
+bool bval
+int ncmd, ival, stat, fd, ip
+pointer sp, cmd, str
+real rval
+bool itob()
+bool streq()
+int strdic(), nscan(), rg_pstati(), btoi(), rg_pregions(), fntopnb()
+int access(), rg_pmkregions(), open(), ctor()
+pointer immap()
+real rg_pstatr()
+errchk immap(), fntopnb()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get the command.
+ call sscan (cmdstr)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call sfree (sp)
+ return
+ }
+
+ # Process the command.
+ ncmd = strdic (Memc[cmd], Memc[cmd], SZ_LINE, PMCMDS)
+ switch (ncmd) {
+ case PMCMD_REFIMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_pstats (pm, REFIMAGE, Memc[str], SZ_FNAME)
+ if (imr == NULL || Memc[cmd] == EOS || streq (Memc[cmd],
+ Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (imr != NULL) {
+ call imunmap (imr)
+ imr = NULL
+ }
+ iferr {
+ imr = immap (Memc[cmd], READ_ONLY, 0)
+ } then {
+ call erract (EA_WARN)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ } else if (IM_NDIM(imr) > 2 || IM_NDIM(imr) != IM_NDIM(im1)) {
+ call printf (
+ "Reference image has the wrong number of dimensions\n")
+ call imunmap (imr)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ } else {
+ call rg_psets (pm, REFIMAGE, Memc[cmd])
+ newref = YES; newdata = YES
+ newfourier = YES; newfilter = YES
+ }
+ }
+
+ case PMCMD_IMAGE:
+
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_pstats (pm, IMAGE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (im1 != NULL) {
+ call imunmap (im1)
+ im1 = NULL
+ }
+ iferr {
+ im1 = immap (Memc[cmd], READ_ONLY, 0)
+ call imseti (im1, IM_TYBNDRY, BT_NEAREST)
+ if (IM_NDIM(im1) == 1)
+ call imseti (im1, IM_NBNDRYPIX, IM_LEN(im1,1))
+ else
+ call imseti (im1, IM_NBNDRYPIX,
+ max (IM_LEN(im1,1), IM_LEN(im1,2)))
+ } then {
+ call erract (EA_WARN)
+ im1 = immap (Memc[str], READ_ONLY, 0)
+ call imseti (im1, IM_TYBNDRY, BT_NEAREST)
+ if (IM_NDIM(im1) == 1)
+ call imseti (im1, IM_NBNDRYPIX, IM_LEN(im1,1))
+ else
+ call imseti (im1, IM_NBNDRYPIX,
+ max (IM_LEN(im1,1), IM_LEN(im1,2)))
+ } else if (IM_NDIM(im1) > 2 || IM_NDIM(im1) != IM_NDIM(imr)) {
+ call printf (
+ "Reference image has the wrong number of dimensions\n")
+ call imunmap (im1)
+ im1 = immap (Memc[str], READ_ONLY, 0)
+ call imseti (im1, IM_TYBNDRY, BT_NEAREST)
+ if (IM_NDIM(im1) == 1)
+ call imseti (im1, IM_NBNDRYPIX, IM_LEN(im1,1))
+ else
+ call imseti (im1, IM_NBNDRYPIX,
+ max (IM_LEN(im1,1), IM_LEN(im1,2)))
+ } else {
+ call rg_psets (pm, IMAGE, Memc[cmd])
+ newdata = YES; newref = YES
+ newfourier = YES; newfilter = YES
+ }
+ }
+
+ case PMCMD_PSFDATA:
+
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_pstats (pm, PSFDATA, Memc[str], SZ_FNAME)
+ if (reglist == NULL || nscan() == 1 || (streq (Memc[cmd],
+ Memc[str]) && Memc[cmd] != EOS)) {
+ call printf ("%s [string/file]: %s\n")
+ call pargstr (KY_PSFDATA)
+ call pargstr (Memc[str])
+ } else if (rg_pstati(pm, CONVOLUTION) == PM_CONIMAGE) {
+ call fntclsb (reglist)
+ iferr {
+ reglist = fntopnb (Memc[cmd], NO)
+ } then {
+ reglist = fntopnb (Memc[str], NO)
+ } else {
+ if (rg_pregions (reglist, imr, pm, 1, NO) > 0)
+ ;
+ call rg_psets (pm, PSFDATA, Memc[cmd])
+ newdata = YES; newref = YES
+ newfourier = YES; newfilter = YES
+ }
+ }
+
+ case PMCMD_PSFIMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_pstats (pm, PSFIMAGE, Memc[str], SZ_FNAME)
+ if (impsf == NULL || Memc[cmd] == EOS || streq (Memc[cmd],
+ Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_PSFIMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (impsf != NULL) {
+ call imunmap (impsf)
+ impsf = NULL
+ }
+ iferr {
+ impsf = immap (Memc[cmd], READ_ONLY, 0)
+ } then {
+ call erract (EA_WARN)
+ impsf = immap (Memc[str], READ_ONLY, 0)
+ } else if (IM_NDIM(impsf) > 2 || IM_NDIM(impsf) !=
+ IM_NDIM(imr)) {
+ call printf (
+ "PSF image has the wrong number of dimensions\n")
+ call imunmap (impsf)
+ impsf = immap (Memc[str], READ_ONLY, 0)
+ } else {
+ call rg_psets (pm, PSFIMAGE, Memc[cmd])
+ newref = YES; newdata = YES
+ newfourier = YES; newfilter = YES
+ }
+ }
+
+ case PMCMD_KERNEL:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_pstats (pm, KERNEL, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_KERNEL)
+ call pargstr (Memc[str])
+ } else {
+ if (imk != NULL) {
+ call imunmap (imk)
+ call imdelete (Memc[str])
+ imk = NULL
+ }
+ iferr {
+ imk = immap (Memc[cmd], NEW_IMAGE, 0)
+ } then {
+ call erract (EA_WARN)
+ imk = NULL
+ call rg_psets (pm, KERNEL, "")
+ } else
+ call rg_psets (pm, KERNEL, Memc[cmd])
+ }
+
+
+ case PMCMD_OUTIMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_pstats (pm, OUTIMAGE, Memc[str], SZ_FNAME)
+ if (im2 == NULL || Memc[cmd] == EOS || streq (Memc[cmd],
+ Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_OUTIMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (im2 != NULL) {
+ call imunmap (im2)
+ im2 = NULL
+ }
+ iferr {
+ im2 = immap (Memc[cmd], NEW_COPY, im1)
+ } then {
+ call erract (EA_WARN)
+ im2 = immap (Memc[str], NEW_COPY, im1)
+ } else {
+ call rg_psets (pm, OUTIMAGE, Memc[cmd])
+ }
+ }
+
+ case PMCMD_DNX:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_DNX)
+ call pargi (rg_pstati (pm, DNX))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, DNX, ival)
+ newref = YES; newdata = YES; newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_DNY:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_DNY)
+ call pargi (rg_pstati (pm, DNY))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, DNY, ival)
+ newref = YES; newdata = YES; newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_PNX:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_PNX)
+ call pargi (rg_pstati (pm, PNX))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, PNX, min (ival, rg_pstati (pm, DNX)))
+ newref = YES; newdata = YES; newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_PNY:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_PNY)
+ call pargi (rg_pstati (pm, PNY))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, PNY, min (ival, rg_pstati(pm, DNY)))
+ newref = YES; newdata = YES; newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_CENTER:
+ call gargb (bval)
+ if (nscan() == 1) {
+ call printf ("%s = %b\n")
+ call pargstr (KY_CENTER)
+ call pargb (itob (rg_pstati (pm, CENTER)))
+ } else {
+ call rg_pseti (pm, CENTER, btoi (bval))
+ newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_BACKGRD:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_pstats (pm, BSTRING, Memc[str], SZ_FNAME)
+ call printf ("%s: %s\n")
+ call pargstr (KY_BACKGRD)
+ call pargstr (Memc[str])
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, PM_BTYPES)
+ ip = 1
+ if (stat > 0) {
+ call rg_pseti (pm, BACKGRD, stat)
+ call rg_psets (pm, BSTRING, Memc[cmd])
+ newfourier = YES; newfilter = YES
+ } else if (ctor (str, ip, rval) > 0) {
+ call rg_psetr (pm, BVALUE, rval)
+ if (ctor (str, ip, rval) > 0) {
+ call rg_psetr (pm, BVALUER, rval)
+ call strcpy (str, PM_BSTRING(pm), SZ_FNAME)
+ call rg_pseti (pm, BACKGRD, PM_NUMBER)
+ } else {
+ call rg_psetr (pm, BVALUE, 0.0)
+ call rg_psetr (pm, BVALUER, 0.0)
+ }
+ }
+ }
+
+ case PMCMD_LOREJECT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_LOREJECT)
+ call pargr (rg_pstatr (pm, LOREJECT))
+ } else {
+ call rg_psetr (pm, LOREJECT, rval)
+ newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_HIREJECT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_HIREJECT)
+ call pargr (rg_pstatr (pm, HIREJECT))
+ } else {
+ call rg_psetr (pm, HIREJECT, rval)
+ newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_APODIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_APODIZE)
+ call pargr (rg_pstatr (pm, APODIZE))
+ } else {
+ call rg_psetr (pm, APODIZE, rval)
+ newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_CONVOLUTION:
+ if (Memc[cmd] == EOS) {
+ call rg_pstats (pm, CSTRING, Memc[str], SZ_LINE)
+ call printf ("%s: %s\n")
+ call pargstr (KY_CONVOLUTION)
+ call pargstr (Memc[str])
+ }
+
+ case PMCMD_UFLUXRATIO:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_UFLUXRATIO)
+ call pargr (rg_pstatr (pm, UFLUXRATIO))
+ } else {
+ call rg_psetr (pm, UFLUXRATIO, rval)
+ newfourier = YES; newfilter = YES
+ }
+
+ case PMCMD_FILTER:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_pstats (pm, FSTRING, Memc[str], SZ_LINE)
+ call printf ("%s: %s\n")
+ call pargstr (KY_FILTER)
+ call pargstr (Memc[str])
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, PM_FTYPES)
+ if (stat > 0) {
+ call rg_pseti (pm, FILTER, stat)
+ call rg_psets (pm, FSTRING, Memc[cmd])
+ }
+ newfilter = YES
+ }
+
+ case PMCMD_SXINNER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_SXINNER)
+ call pargr (rg_pstatr (pm, SXINNER))
+ } else {
+ call rg_psetr (pm, SXINNER, rval)
+ newfilter = YES
+ }
+
+ case PMCMD_SXOUTER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_SXOUTER)
+ call pargr (rg_pstatr (pm, SXOUTER))
+ } else {
+ call rg_psetr (pm, SXOUTER, rval)
+ newfilter = YES
+ }
+
+ case PMCMD_SYINNER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_SYINNER)
+ call pargr (rg_pstatr (pm, SYINNER))
+ } else {
+ call rg_psetr (pm, SYINNER, rval)
+ newfilter = YES
+ }
+
+ case PMCMD_SYOUTER:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_SYOUTER)
+ call pargr (rg_pstatr (pm, SYOUTER))
+ } else {
+ call rg_psetr (pm, SYOUTER, rval)
+ newfilter = YES
+ }
+
+ case PMCMD_RADSYM:
+ call gargb (bval)
+ if (nscan() == 1) {
+ call printf ("%s = %b\n")
+ call pargstr (KY_RADSYM)
+ call pargb (itob (rg_pstati (pm, RADSYM)))
+ } else {
+ call rg_pseti (pm, RADSYM, btoi (bval))
+ newfilter = YES
+ }
+
+ case PMCMD_THRESHOLD:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_THRESHOLD)
+ call pargr (rg_pstatr (pm, THRESHOLD))
+ } else {
+ call rg_psetr (pm, THRESHOLD, rval)
+ newfilter = YES
+ }
+
+ case PMCMD_NORMFACTOR:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_NORMFACTOR)
+ call pargr (rg_pstatr (pm, NORMFACTOR))
+ } else {
+ call rg_psetr (pm, NORMFACTOR, rval)
+ newfilter = YES
+ }
+
+ case PMCMD_SHOW:
+ call gdeactivate (gd, 0)
+ call rg_pshow (pm)
+ call greactivate (gd, 0)
+
+ case PMCMD_MARK:
+ call gdeactivate (gd, 0)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ fd = NULL
+ } else if (access (Memc[cmd], 0, 0) == YES) {
+ call printf ("Warning: file %s already exists\n")
+ call pargstr (Memc[cmd])
+ fd = NULL
+ } else {
+ fd = open (Memc[cmd], NEW_FILE, TEXT_FILE)
+ }
+ call printf ("\n")
+ if (rg_pmkregions (fd, imr, pm, 1, MAX_NREGIONS) <= 0)
+ call printf ("The regions list is empty\n")
+ newdata = YES; newref = YES
+ newfourier = YES; newfilter = YES
+ call printf ("\n")
+ if (fd != NULL)
+ call close (fd)
+ call greactivate (gd, 0)
+
+ default:
+ call printf ("Unknown or ambiguous colon command\7\n")
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgpconvolve.x b/pkg/images/immatch/src/psfmatch/rgpconvolve.x
new file mode 100644
index 00000000..6b516a95
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpconvolve.x
@@ -0,0 +1,106 @@
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+
+# RG_PCONVOLVE -- Convolve an image with an nxk by nyk kernel. The kernel
+# dimensions are assumed to be odd.
+
+procedure rg_pconvolve (im1, im2, kernel, nxk, nyk, boundary, constant)
+
+pointer im1 # pointer to the input image
+pointer im2 # pointer to the output image
+real kernel[nxk,nyk] # the convolution kernel
+int nxk, nyk # dimensions of the kernel
+int boundary # type of boundary extension
+real constant # constant for constant boundary extension
+
+int i, ncols, nlines, col1, col2, nincols, inline, outline
+pointer sp, lineptrs, linebuf, outbuf, nkern
+pointer imgs2r(), impl2r()
+errchk imgs2r, impl2r
+
+begin
+ # Set up an array of line pointers.
+ call smark (sp)
+ call salloc (lineptrs, nyk, TY_POINTER)
+ call salloc (nkern, nxk * nyk, TY_REAL)
+
+ # Set the number of image buffers.
+ call imseti (im1, IM_NBUFS, nyk)
+
+ # Set the input image boundary conditions.
+ call imseti (im1, IM_TYBNDRY, boundary)
+ call imseti (im1, IM_NBNDRYPIX, max (nxk / 2 + 1, nyk / 2 + 1))
+ if (boundary == BT_CONSTANT)
+ call imsetr (im1, IM_BNDRYPIXVAL, constant)
+
+ # Define the number of output image lines and columns.
+ ncols = IM_LEN(im2,1)
+ if (IM_NDIM(im2) == 1)
+ nlines = 1
+ else
+ nlines = IM_LEN(im2,2)
+
+ # Set the input image column limits.
+ col1 = 1 - nxk / 2
+ col2 = IM_LEN(im1,1) + nxk / 2
+ nincols = col2 - col1 + 1
+
+ # Flip the kernel
+ call rg_pflip (kernel, Memr[nkern], nxk, nyk)
+
+ # Initialise the line buffers.
+ inline = 1 - nyk / 2
+ do i = 1 , nyk - 1 {
+ Memi[lineptrs+i] = imgs2r (im1, col1, col2, inline, inline)
+ inline = inline + 1
+ }
+
+ # Generate the output image line by line
+ call salloc (linebuf, nincols, TY_REAL)
+ do outline = 1, nlines {
+
+ # Scroll the input buffers
+ do i = 1, nyk - 1
+ Memi[lineptrs+i-1] = Memi[lineptrs+i]
+
+ # Read in new image line
+ Memi[lineptrs+nyk-1] = imgs2r (im1, col1, col2, inline,
+ inline)
+
+ # Get output image line
+ outbuf = impl2r (im2, outline)
+ if (outbuf == EOF)
+ call error (0, "Error writing output image.")
+
+ # Generate output image line
+ call aclrr (Memr[outbuf], ncols)
+ do i = 1, nyk
+ call acnvr (Memr[Memi[lineptrs+i-1]], Memr[outbuf], ncols,
+ Memr[nkern+(i-1)*nxk], nxk)
+
+ inline = inline + 1
+ }
+
+ # Free the image buffer pointers
+ call sfree (sp)
+end
+
+
+# RG_PFLIP -- Flip the kernel in preparation for convolution.
+
+procedure rg_pflip (inkern, outkern, nxk, nyk)
+
+real inkern[nxk,nyk] # the input kernel
+real outkern[nxk,nyk] # the output kernel
+int nxk, nyk # the kernel dimensions
+
+int i, j
+
+begin
+ do j = 1, nyk {
+ do i = 1, nxk {
+ outkern[i,j] = inkern[nxk+1-i,nyk+1-j]
+ }
+ }
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgpfft.x b/pkg/images/immatch/src/psfmatch/rgpfft.x
new file mode 100644
index 00000000..b5f36375
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpfft.x
@@ -0,0 +1,443 @@
+
+# RG_PG10F -- Fetch the 0 component of the fft.
+
+real procedure rg_pg10f (fft, nxfft, nyfft)
+
+real fft[nxfft,nyfft] #I array containing 2 real ffts
+int nxfft #I x dimension of complex array
+int nyfft #I y dimension of complex array
+
+int xcen, ycen
+
+begin
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+
+ return (fft[xcen,ycen])
+end
+
+
+# RG_PG1NORM -- Estimate the normalization factor by computing the amplitude
+# of the best fitting Gaussian. This routine may eventually be replaced by
+# on which does a complete Gaussian fit. The Gaussian is assumed to be
+# of the form g = a * exp (b * r * r). The input array is a 2D real array
+# storing 1 fft of dimension nxfft by nyfft in complex order with the
+# zero frequency in the center.
+
+real procedure rg_pg1norm (fft, nxfft, nyfft)
+
+real fft[nxfft,nyfft] #I array containing 2 real ffts
+int nxfft #I x dimension of complex array
+int nyfft #I y dimension of complex array
+
+int xcen, ycen
+real ln1, ln2, cx, cy
+
+begin
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+
+ if (nxfft >= 8) {
+ ln1 = log (sqrt (fft[xcen-2,ycen] ** 2 + fft[xcen-1,ycen] ** 2))
+ ln2 = log (sqrt (fft[xcen-4,ycen] ** 2 + fft[xcen-3,ycen] ** 2))
+ cx = exp ((4.0 * ln1 - ln2) / 3.0)
+ } else
+ cx = 0.0
+
+ if (nyfft >= 4) {
+ ln1 = log (sqrt (fft[xcen,ycen-1] ** 2 + fft[xcen+1,ycen-1] ** 2))
+ ln2 = log (sqrt (fft[xcen,ycen-2] ** 2 + fft[xcen+1,ycen-2] ** 2))
+ cy = exp ((4.0 * ln1 - ln2) / 3.0)
+ } else
+ cy = 0.0
+
+ if (cx <= 0.0)
+ return (cy)
+ else if (cy <= 0.0)
+ return (cx)
+ else
+ return (0.5 * (cx + cy))
+end
+
+
+# RG_PG20F -- Fetch the 0 component of the fft.
+
+real procedure rg_pg20f (fft, nxfft, nyfft)
+
+real fft[nxfft,nyfft] #I array containing 2 real ffts
+int nxfft #I x dimension of complex array
+int nyfft #I y dimension of complex array
+
+int xcen, ycen
+
+begin
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+
+ return (fft[xcen,ycen] / fft[xcen+1,ycen])
+end
+
+
+# RG_PG2NORM -- Estimate the normalization factor by computing the amplitude
+# of the best fitting Gaussian. This routine may eventually be replaced by
+# on which does a complete Gaussian fit. The Gaussian is assumed to be
+# of the form g = a * exp (b * r * r). The input array is a 2D real array
+# storing 2 2D ffts of dimension nxfft by nyfft in complex order with the
+# zero frequency in the center.
+
+real procedure rg_pg2norm (fft, nxfft, nyfft)
+
+real fft[nxfft,nyfft] #I array containing 2 real ffts
+int nxfft #I x dimension of complex array
+int nyfft #I y dimension of complex array
+
+int xcen, ycen
+real fftr, ffti, ln1r, ln2r, ln1i, ln2i, cxr, cyr, cxi, cyi, ampr, ampi
+
+begin
+
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+
+ # Compute the x amplitude for the first fft.
+ if (nxfft >= 8) {
+
+ fftr = 0.5 * (fft[xcen+2,ycen] + fft[xcen-2,ycen])
+ ffti = 0.5 * (fft[xcen+3,ycen] - fft[xcen-1,ycen])
+ ln1r = log (sqrt (fftr ** 2 + ffti ** 2))
+ fftr = 0.5 * (fft[xcen+4,ycen] + fft[xcen-4,ycen])
+ ffti = 0.5 * (fft[xcen+5,ycen] - fft[xcen-3,ycen])
+ ln2r = log (sqrt (fftr ** 2 + ffti ** 2))
+
+ fftr = 0.5 * (fft[xcen+3,ycen] + fft[xcen-1,ycen])
+ ffti = -0.5 * (fft[xcen+2,ycen] - fft[xcen-2,ycen])
+ ln1i = log (sqrt (fftr ** 2 + ffti ** 2))
+ fftr = 0.5 * (fft[xcen+5,ycen] + fft[xcen-3,ycen])
+ ffti = -0.5 * (fft[xcen+4,ycen] - fft[xcen-4,ycen])
+ ln2i = log (sqrt (fftr ** 2 + ffti ** 2))
+
+ cxr = exp ((4.0 * ln1r - ln2r) / 3.0)
+ cxi = exp ((4.0 * ln1i - ln2i) / 3.0)
+
+ } else {
+
+ cxr = 0.0
+ cxi = 0.0
+
+ }
+
+ # Compute the y ratio.
+ if (nyfft >= 4) {
+
+ fftr = 0.5 * (fft[xcen,ycen+1] + fft[xcen,ycen-1])
+ ffti = 0.5 * (fft[xcen+1,ycen+1] - fft[xcen+1,ycen-1])
+ ln1r = log (sqrt (fftr ** 2 + ffti ** 2))
+ fftr = 0.5 * (fft[xcen,ycen+2] + fft[xcen,ycen-2])
+ ffti = 0.5 * (fft[xcen+1,ycen+2] - fft[xcen+1,ycen-2])
+ ln2r = log (sqrt (fftr ** 2 + ffti ** 2))
+
+ fftr = 0.5 * (fft[xcen+1,ycen+1] + fft[xcen+1,ycen-1])
+ ffti = -0.5 * (fft[xcen,ycen+1] - fft[xcen,ycen-1])
+ ln1i = log (sqrt (fftr ** 2 + ffti ** 2))
+ fftr = 0.5 * (fft[xcen+1,ycen+2] + fft[xcen+1,ycen-2])
+ ffti = -0.5 * (fft[xcen,ycen+2] - fft[xcen,ycen-2])
+ ln2i = log (sqrt (fftr ** 2 + ffti ** 2))
+
+ cyr = exp ((4.0 * ln1r - ln2r) / 3.0)
+ cyi = exp ((4.0 * ln1i - ln2i) / 3.0)
+
+ } else {
+
+ cyr = 0.0
+ cyi = 0.0
+
+ }
+
+ if (cxr <= 0.0)
+ ampr = cyr
+ else if (cyr <= 0.0)
+ ampr = cxr
+ else
+ ampr = 0.5 * (cxr + cyr)
+
+ if (cxi <= 0.0)
+ ampi = cyi
+ else if (cyi <= 0.0)
+ ampi = cxi
+ else
+ ampi = 0.5 * (cxi + cyi)
+
+ if (ampi <= 0.0)
+ return (INDEFR)
+ else
+ return (ampr /ampi)
+end
+
+
+# RG_PDIVFFT -- Unpack the two fft's, save the first fft, and compute the
+# quotient of the two ffts.
+
+procedure rg_pdivfft (fft1, fftnum, fftdenom, fft2, nxfft, nyfft)
+
+real fft1[nxfft,nyfft] # array containing 2 ffts of 2 real functions
+real fftnum[nxfft,nyfft] # the numerator fft
+real fftdenom[nxfft,nyfft] # the denominator fft
+real fft2[nxfft,nyfft] # fft of psf matching function
+int nxfft, nyfft # dimensions of fft
+
+int i, j, xcen, ycen, nxp2, nxp3, nyp2
+real c1, c2, h1r, h1i, h2r, h2i, denom
+
+begin
+ c1 = 0.5
+ c2 = -0.5
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+ nxp2 = nxfft + 2
+ nxp3 = nxfft + 3
+ nyp2 = nyfft + 2
+
+ # Compute the 0 frequency point.
+ h1r = fft1[xcen,ycen]
+ h1i = 0.0
+ h2r = fft1[xcen+1,ycen]
+ h2i = 0.0
+ fftnum[xcen,ycen] = h1r
+ fftnum[xcen+1,ycen] = 0.0
+ fftdenom[xcen,ycen] = h2r
+ fftdenom[xcen+1,ycen] = 0.0
+ fft2[xcen,ycen] = h1r / h2r
+ fft2[xcen+1,ycen] = 0.0
+
+ #call eprintf ("fft11=%g fft21=%g\n")
+ #call pargr (fft1[1,1])
+ #call pargr (fft1[2,1])
+
+ # Compute the first point.
+ h1r = c1 * (fft1[1,1] + fft1[1,1])
+ h1i = 0.0
+ h2r = -c2 * (fft1[2,1] + fft1[2,1])
+ h2i = 0.0
+
+ fftnum[1,1] = h1r
+ fftnum[2,1] = h1i
+ fftdenom[1,1] = h2r
+ fftdenom[2,1] = h2i
+ denom = h2r * h2r + h2i * h2i
+ if (denom == 0.0) {
+ fft2[1,1] = 1.0
+ fft2[2,1] = 0.0
+ } else {
+ fft2[1,1] = (h1r * h2r + h1i * h2i) / denom
+ fft2[2,1] = (h1i * h2r - h2i * h1r) / denom
+ }
+
+ # Compute the x symmetry axis points.
+ do i = 3, xcen - 1, 2 {
+
+ h1r = c1 * (fft1[i,ycen] + fft1[nxp2-i,ycen])
+ h1i = c1 * (fft1[i+1,ycen] - fft1[nxp3-i,ycen])
+ h2r = -c2 * (fft1[i+1,ycen] + fft1[nxp3-i,ycen])
+ h2i = c2 * (fft1[i,ycen] - fft1[nxp2-i,ycen])
+
+ fftnum[i,ycen] = h1r
+ fftnum[i+1,ycen] = h1i
+ fftnum[nxp2-i,ycen] = h1r
+ fftnum[nxp3-i,ycen] = -h1i
+
+ fftdenom[i,ycen] = h2r
+ fftdenom[i+1,ycen] = h2i
+ fftdenom[nxp2-i,ycen] = h2r
+ fftdenom[nxp3-i,ycen] = -h2i
+
+ denom = h2r * h2r + h2i * h2i
+ if (denom == 0.0) {
+ fft2[i,ycen] = 1.0
+ fft2[i+1,ycen] = 0.0
+ } else {
+ fft2[i,ycen] = (h1r * h2r + h1i * h2i) / denom
+ fft2[i+1,ycen] = (h1i * h2r - h2i * h1r) / denom
+ }
+ fft2[nxp2-i,ycen] = fft2[i,ycen]
+ fft2[nxp3-i,ycen] = -fft2[i+1,ycen]
+
+ }
+
+ # Quit if the transform is 1D.
+ if (nyfft < 2)
+ return
+
+ # Compute the x axis points.
+ do i = 3, xcen + 1, 2 {
+
+ h1r = c1 * (fft1[i,1] + fft1[nxp2-i,1])
+ h1i = c1 * (fft1[i+1,1] - fft1[nxp3-i,1])
+ h2r = -c2 * (fft1[i+1,1] + fft1[nxp3-i,1])
+ h2i = c2 * (fft1[i,1] - fft1[nxp2-i,1])
+
+ fftnum[i,1] = h1r
+ fftnum[i+1,1] = h1i
+ fftnum[nxp2-i,1] = h1r
+ fftnum[nxp3-i,1] = -h1i
+
+ fftdenom[i,1] = h2r
+ fftdenom[i+1,1] = h2i
+ fftdenom[nxp2-i,1] = h2r
+ fftdenom[nxp3-i,1] = -h2i
+
+ denom = h2r * h2r + h2i * h2i
+ if (denom == 0) {
+ fft2[i,1] = 1.0
+ fft2[i+1,1] = 0.0
+ } else {
+ fft2[i,1] = (h1r * h2r + h1i * h2i) / denom
+ fft2[i+1,1] = (h1i * h2r - h2i * h1r) / denom
+ }
+ fft2[nxp2-i,1] = fft2[i,1]
+ fft2[nxp3-i,1] = -fft2[i+1,1]
+ }
+
+ # Compute the y symmetry axis points.
+ do i = 2, ycen - 1 {
+
+ h1r = c1 * (fft1[xcen,i] + fft1[xcen, nyp2-i])
+ h1i = c1 * (fft1[xcen+1,i] - fft1[xcen+1,nyp2-i])
+ h2r = -c2 * (fft1[xcen+1,i] + fft1[xcen+1,nyp2-i])
+ h2i = c2 * (fft1[xcen,i] - fft1[xcen,nyp2-i])
+
+ fftnum[xcen,i] = h1r
+ fftnum[xcen+1,i] = h1i
+ fftnum[xcen,nyp2-i] = h1r
+ fftnum[xcen+1,nyp2-i] = -h1i
+
+ fftdenom[xcen,i] = h2r
+ fftdenom[xcen+1,i] = h2i
+ fftdenom[xcen,nyp2-i] = h2r
+ fftdenom[xcen+1,nyp2-i] = -h2i
+
+ denom = h2r * h2r + h2i * h2i
+ if (denom == 0.0) {
+ fft2[xcen,i] = 1.0
+ fft2[xcen+1,i] = 0.0
+ } else {
+ fft2[xcen,i] = (h1r * h2r + h1i * h2i) / denom
+ fft2[xcen+1,i] = (h1i * h2r - h2i * h1r) / denom
+ }
+ fft2[xcen,nyp2-i] = fft2[xcen,i]
+ fft2[xcen+1,nyp2-i] = -fft2[xcen+1,i]
+
+ }
+
+ # Compute the y axis points.
+ do i = 2, ycen {
+
+ h1r = c1 * (fft1[1,i] + fft1[1,nyp2-i])
+ h1i = c1 * (fft1[2,i] - fft1[2,nyp2-i])
+ h2r = -c2 * (fft1[2,i] + fft1[2,nyp2-i])
+ h2i = c2 * (fft1[1,i] - fft1[1,nyp2-i])
+
+ fftnum[1,i] = h1r
+ fftnum[2,i] = h1i
+ fftnum[1,nyp2-i] = h1r
+ fftnum[2,nyp2-i] = -h1i
+
+ fftdenom[1,i] = h2r
+ fftdenom[2,i] = h2i
+ fftdenom[1,nyp2-i] = h2r
+ fftdenom[2,nyp2-i] = -h2i
+
+ denom = h2r * h2r + h2i * h2i
+ if (denom == 0.0) {
+ fft2[1,i] = 1.0
+ fft2[2,i] = 0.0
+ } else {
+ fft2[1,i] = (h1r * h2r + h1i * h2i) / denom
+ fft2[2,i] = (h1i * h2r - h2i * h1r) / denom
+ }
+ fft2[1,nyp2-i] = fft2[1,i]
+ fft2[2,nyp2-i] = -fft2[2,i]
+ }
+
+ # Compute the remainder of the transform.
+ do j = 2, ycen - 1 {
+
+ do i = 3, xcen - 1, 2 {
+
+ h1r = c1 * (fft1[i,j] + fft1[nxp2-i, nyp2-j])
+ h1i = c1 * (fft1[i+1,j] - fft1[nxp3-i,nyp2-j])
+ h2r = -c2 * (fft1[i+1,j] + fft1[nxp3-i,nyp2-j])
+ h2i = c2 * (fft1[i,j] - fft1[nxp2-i,nyp2-j])
+
+ fftnum[i,j] = h1r
+ fftnum[i+1,j] = h1i
+ fftnum[nxp2-i,nyp2-j] = h1r
+ fftnum[nxp3-i,nyp2-j] = -h1i
+
+ fftdenom[i,j] = h2r
+ fftdenom[i+1,j] = h2i
+ fftdenom[nxp2-i,nyp2-j] = h2r
+ fftdenom[nxp3-i,nyp2-j] = -h2i
+
+ denom = h2r * h2r + h2i * h2i
+ if (denom == 0.0) {
+ fft2[i,j] = 1.0
+ fft2[i+1,j] = 0.0
+ } else {
+ fft2[i,j] = (h1r * h2r + h1i * h2i) / denom
+ fft2[i+1,j] = (h1i * h2r - h2i * h1r) / denom
+ }
+ fft2[nxp2-i,nyp2-j] = fft2[i,j]
+ fft2[nxp3-i,nyp2-j] = - fft2[i+1,j]
+ }
+
+ do i = xcen + 2, nxfft, 2 {
+
+ h1r = c1 * (fft1[i,j] + fft1[nxp2-i, nyp2-j])
+ h1i = c1 * (fft1[i+1,j] - fft1[nxp3-i,nyp2-j])
+ h2r = -c2 * (fft1[i+1,j] + fft1[nxp3-i,nyp2-j])
+ h2i = c2 * (fft1[i,j] - fft1[nxp2-i,nyp2-j])
+
+ fftnum[i,j] = h1r
+ fftnum[i+1,j] = h1i
+ fftnum[nxp2-i,nyp2-j] = h1r
+ fftnum[nxp3-i,nyp2-j] = -h1i
+
+ fftdenom[i,j] = h2r
+ fftdenom[i+1,j] = h2i
+ fftdenom[nxp2-i,nyp2-j] = h2r
+ fftdenom[nxp3-i,nyp2-j] = -h2i
+
+ denom = h2r * h2r + h2i * h2i
+ if (denom == 0.0) {
+ fft2[i,j] = 1.0
+ fft2[i+1,j] = 0.0
+ } else {
+ fft2[i,j] = (h1r * h2r + h1i * h2i) / denom
+ fft2[i+1,j] = (h1i * h2r - h2i * h1r) / denom
+ }
+ fft2[nxp2-i,nyp2-j] = fft2[i,j]
+ fft2[nxp3-i,nyp2-j] = - fft2[i+1,j]
+
+ }
+ }
+end
+
+
+# RG_PNORM -- Insert the normalization value into the 0 frequency of the
+# fft. The fft is a 2D fft stored in a real array in complex order.
+# The fft is assumed to be centered.
+
+procedure rg_pnorm (fft, nxfft, nyfft, norm)
+
+real fft[ARB] #I the input fft
+int nxfft #I the x dimension of fft (complex storage)
+int nyfft #I the y dimension of the fft
+real norm #I the flux ratio
+
+int index
+
+begin
+ index = nxfft + 1 + 2 * (nyfft / 2) * nxfft
+ fft[index] = norm
+ fft[index+1] = 0.0
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgpfilter.x b/pkg/images/immatch/src/psfmatch/rgpfilter.x
new file mode 100644
index 00000000..63040b63
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpfilter.x
@@ -0,0 +1,502 @@
+include <math.h>
+
+# RG_PCOSBELL -- Apply a cosine bell function to the data.
+
+procedure rg_pcosbell (fft, nxfft, nyfft, sx1, sx2, sy1, sy2, radsym)
+
+real fft[ARB] #I/O the ifft to be filtered
+int nxfft #I the x dimension of the fft
+int nyfft #I the y dimension of the fft
+real sx1 #I inner x radius of the cosine bell filter
+real sx2 #I outer x radius of the cosine bell filter
+real sy1 #I inner y radius of the cosine bell filter
+real sy2 #I outer y radius of the cosine bell filter
+int radsym #I radial symmetry ?
+
+int i, j, index, xcen, ycen
+real factorx, factory, r1, r2, r, rj, cos2
+
+begin
+ # Compute the center of the fft.
+ xcen = (nxfft / 2) + 1
+ ycen = (nyfft / 2) + 1
+
+ if (radsym == NO) {
+
+ # Filter in the y direction independently.
+ if (IS_INDEFR(sy1))
+ r1 = 0.0
+ else
+ r1 = sy1
+ if (IS_INDEFR(sy2))
+ r2 = nyfft - ycen + 1
+ else
+ r2 = sy2
+ factory = HALFPI / (r2 - r1)
+ index = 1
+ do j = 1, nyfft {
+ r = abs (ycen - j)
+ if (r >= r2)
+ cos2 = 0.0
+ else if (r <= r1)
+ cos2 = 1.0
+ else
+ cos2 = cos ((r - r1) * factory) ** 2
+ call amulkr (fft[index], cos2, fft[index], 2 * nxfft)
+ index = index + 2 * nxfft
+ }
+
+ # Filter in the x direction independently.
+ if (IS_INDEFR(sx1))
+ r1 = 0.0
+ else
+ r1 = sx1
+ if (IS_INDEFR(sx2))
+ r2 = nxfft - xcen + 1
+ else
+ r2 = sx2
+ factorx = HALFPI / (r2 - r1)
+
+ do i = 1, nxfft {
+ r = abs (xcen - i)
+ if (r >= r2)
+ cos2 = 0.0
+ else if (r <= r1)
+ cos2 = 1.0
+ else
+ cos2 = cos ((r - r1) * factorx) ** 2
+ do j = 2 * i - 1, 2 * nxfft * nyfft, 2 * nxfft {
+ fft[j] = fft[j] * cos2
+ fft[j+1] = fft[j+1] * cos2
+ }
+ }
+
+ } else {
+
+ if (IS_INDEFR(sx1) && IS_INDEFR(sy1))
+ r1 = 0.0
+ else if (IS_INDEFR(sx1))
+ r1 = sy1
+ else if (IS_INDEFR(sy1))
+ r1 = sx1
+ else
+ r1 = (sx1 + sy1) / 2.0
+ if (IS_INDEFR(sx2) && IS_INDEFR(sy2))
+ r2 = (nxfft - xcen + 1 + nyfft - ycen + 1) / 2.0
+ else if (IS_INDEFR(sx2))
+ r2 = sy2
+ else if (IS_INDEFR(sy2))
+ r2 = sx2
+ else
+ r2 = (sx2 + sy2) / 2.0
+ factorx = HALFPI / (r2 - r1)
+
+ index = 0
+ do j = 1, nyfft {
+ rj = (ycen - j) ** 2
+ do i = 1, nxfft {
+ r = sqrt ((i - xcen) ** 2 + rj)
+ if (r >= r2) {
+ fft[index+2*i-1] = 0.0
+ fft[index+2*i] = 0.0
+ } else if (r > r1) {
+ fft[index+2*i-1] = fft[index+2*i-1] * cos ((r - r1) *
+ factorx) ** 2
+ fft[index+2*i] = fft[index+2*i] * cos ((r - r1) *
+ factorx) ** 2
+ }
+ }
+ index = index + 2 * nxfft
+ }
+ }
+end
+
+
+# RG_PREPLACE -- Replace low valued regions in the kernel fft with a Gaussian
+# extension.
+
+procedure rg_preplace (fft, fftdiv, nxfft, nyfft, pthreshold, norm)
+
+real fft[ARB] #I/O the fft of the kernel
+real fftdiv[ARB] #I the divisor fft
+int nxfft #I x dimension of the fft (complex storage)
+int nyfft #I y dimension of the fft
+real pthreshold #I the minimum percent amplitude in the divisor
+real norm #I the normalization value
+
+pointer sp, params
+int xcen, ycen, i, j, ri, rj, index
+real divpeak, a1, a2, a3, u, v, divisor, absv, phi
+
+begin
+ call smark (sp)
+ call salloc (params, 5, TY_REAL)
+
+ # Compute the central amplitude peak.
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+ divpeak = pthreshold * fftdiv[1+nxfft+2*(ycen-1)*nxfft]
+
+ # Fit the parameters.
+ call rg_pgaussfit (fft, fftdiv, nxfft, nyfft, divpeak, norm,
+ Memr[params])
+
+ # Store the parameters in temporary variables.
+ a1 = Memr[params]
+ a2 = Memr[params+1]
+ a3 = Memr[params+2]
+ u = Memr[params+3]
+ v = Memr[params+4]
+
+ # Perform the extension.
+ index = 0
+ do j = 1, nyfft {
+ rj = j - ycen
+ do i = 1, nxfft {
+ ri = i - xcen
+ divisor = sqrt (fftdiv[index+2*i-1] ** 2 +
+ fftdiv[index+2*i] ** 2)
+ if (divisor < divpeak) {
+ absv = norm * exp (a1 * ri * ri + a2 * ri * rj + a3 *
+ rj * rj)
+ phi = u * ri + v * rj
+ fft[index+2*i-1] = absv * cos (phi)
+ fft[index+2*i] = absv * sin (phi)
+ }
+ }
+ index = index + 2 * nxfft
+ }
+
+ # Correct the first row.
+ do i = 1, 2 * nxfft, 2 {
+ fft[i] = sqrt (fft[i] ** 2 + fft[i+1] ** 2)
+ fft[i+1] = 0.0
+ }
+
+ # Correct the first column.
+ index = 1
+ do j = 2, nyfft {
+ fft[index] = sqrt (fft[index] ** 2 + fft[index+1] ** 2)
+ fft[index+1] = 0.0
+ index = index + 2 * nxfft
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_PGMODEL -- Replace low values with a Gaussian mode.
+
+procedure rg_pgmodel (fft, fftdiv, nxfft, nyfft, pthreshold, norm)
+
+real fft[ARB] #I/O the fft of the kernel
+real fftdiv[ARB] #I the divisor fft
+int nxfft #I the x dimension of the fft
+int nyfft #I the y dimension of the fft
+real pthreshold #I the minimum percent amplitude in the divisor
+real norm #I the normalization factor
+
+pointer sp, params
+int xcen, ycen, i, j, index
+real divpeak, a1, a2, a3, u, v, absv, phi, ri, rj
+
+begin
+ call smark (sp)
+ call salloc (params, 5, TY_REAL)
+
+ # Compute the central amplitude peak.
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+ divpeak = pthreshold * fftdiv[1+nxfft+2*(ycen-1)*nxfft]
+
+ # Fit the parameters.
+ call rg_pgaussfit (fft, fftdiv, nxfft, nyfft, divpeak, norm,
+ Memr[params])
+
+ # Store the parameters in temporary variables
+ a1 = Memr[params]
+ a2 = Memr[params+1]
+ a3 = Memr[params+2]
+ u = Memr[params+3]
+ v = Memr[params+4]
+
+ # Perform the extension.
+ index = 0
+ do j = 1, nyfft {
+ rj = j - ycen
+ do i = 1, nxfft {
+ ri = i - xcen
+ absv = norm * exp (a1 * ri * ri + a2 * ri * rj + a3 * rj * rj)
+ phi = u * ri + v * rj
+ fft[index+2*i-1] = absv * cos (phi)
+ fft[index+2*i] = absv * sin (phi)
+ }
+ index = index + 2 * nxfft
+ }
+
+ # Correct the first row.
+ do i = 1, 2 * nxfft, 2 {
+ fft[i] = sqrt (fft[i] ** 2 + fft[i+1] ** 2)
+ fft[i+1] = 0.0
+ }
+
+ # Correct the first column.
+ index = 1
+ do j = 2, nyfft {
+ fft[index] = sqrt (fft[index] ** 2 + fft[index+1] ** 2)
+ fft[index+1] = 0.0
+ index = index + 2 * nxfft
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_PGAUSSFIT -- Procedure to compute the Gaussian parameters
+
+procedure rg_pgaussfit (fft, fftdiv, nxfft, nyfft, divpeak, norm, param)
+
+real fft[ARB] #I the fft of the kernel
+real fftdiv[ARB] #I the divisor fft
+int nxfft #I the x dimension of the fft
+int nyfft #I the y dimension of the fft
+real divpeak #I the minimum value in the divisor
+real norm #I the normalization value norm value
+real param[ARB] #O the output fitted parameters
+
+int i, j, yj, xcen, ycen
+double x, y, x2, xy, y2, z, wt, x2w, y2w, xyw, zw, xzw, yzw
+double sxxxx, sxxxy, sxxyy, sxyyy, syyyy, sxxz, sxyz, syyz, sxx, sxy
+double syy, sxz, syz
+pointer sp, mat
+real divisor
+
+begin
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (mat, 12, TY_DOUBLE)
+
+ # Define the center of the fft.
+ xcen = nxfft / 2 + 1
+ ycen = nyfft / 2 + 1
+
+ # Initialize.
+ sxxxx = 0.0d0
+ sxxxy = 0.0d0
+ sxxyy = 0.0d0
+ sxyyy = 0.0d0
+ syyyy = 0.0d0
+ sxxz = 0.0d0
+ sxyz = 0.0d0
+ syyz = 0.0d0
+ sxx = 0.0d0
+ sxy = 0.0d0
+ syy = 0.0d0
+ sxz = 0.0d0
+ syz = 0.0d0
+
+ do i = 1, nxfft {
+ x = i - xcen
+ yj = - ycen
+ do j = 2 * i - 1, 2 * nxfft * nyfft, 2 * nxfft {
+ yj = yj + 1
+ y = yj
+
+ # Skip low points in the fit.
+ divisor = sqrt (fftdiv[j] ** 2 + fftdiv[j+1] ** 2)
+ if (divisor < divpeak)
+ next
+ if (i == xcen || yj == ycen)
+ next
+
+ # Accumulate the intermediate products.
+ divisor = sqrt (fft[j] ** 2 + fft[j+1] ** 2)
+ if (divisor <= 0.0)
+ next
+ z = log (divisor / norm)
+ x2 = x * x
+ y2 = y * y
+ wt = 1.0 / sqrt (x2 + y2)
+ xy = x * y
+ x2w = x2 * wt
+ y2w = y2 * wt
+ xyw = xy * wt
+ zw = z * wt
+ xzw = x * zw
+ yzw = y * zw
+
+ # Accumulate the sums for the Gaussian.
+ sxxxx = sxxxx + x2 * x2w
+ sxxxy = sxxxy + x2 * xyw
+ sxxyy = sxxyy + x2 * y2w
+ sxyyy = sxyyy + xy * y2w
+ syyyy = syyyy + y2 * y2w
+ sxxz = sxxz + x * xzw
+ sxyz = sxyz + x * yzw
+ syyz = syyz + y * yzw
+
+ # New weight and z point.
+ wt = sqrt (fft[j] ** 2 + fft[j+1] ** 2) / norm
+ z = atan2 (fft[j+1], fft[j])
+
+ # Accumulate the sums for the shift determinantion.
+ sxx = sxx + x2 * wt
+ sxy = sxy + xy * wt
+ syy = syy + y2 * wt
+ sxz = sxz + x * z * wt
+ syz = syz + y * z * wt
+ }
+ }
+
+ # Solve for the gaussian.
+ Memd[mat] = sxxxx
+ Memd[mat+1] = sxxxy
+ Memd[mat+2] = sxxyy
+ Memd[mat+3] = sxxz
+ Memd[mat+4] = sxxxy
+ Memd[mat+5] = sxxyy
+ Memd[mat+6] = sxyyy
+ Memd[mat+7] = sxyz
+ Memd[mat+8] = sxxyy
+ Memd[mat+9] = sxyyy
+ Memd[mat+10] = syyyy
+ Memd[mat+11] = syyz
+ call rg_pgelim (Memd[mat], 3)
+ param[1] = Memd[mat+3]
+ param[2] = Memd[mat+7]
+ param[3] = Memd[mat+11]
+
+ # Solve for the shift.
+ Memd[mat] = sxx
+ Memd[mat+1] = sxy
+ Memd[mat+2] = sxz
+ Memd[mat+3] = sxy
+ Memd[mat+4] = syy
+ Memd[mat+5] = syz
+ call rg_pgelim (Memd[mat], 2)
+ param[4] = Memd[mat+2]
+ param[5] = Memd[mat+5]
+
+ call sfree (sp)
+end
+
+
+# RG_PGELIM -- Solve a matrix using Gaussian elimination.
+
+procedure rg_pgelim (a, n)
+
+double a[n+1,n] #I/O matrix to be solved
+int n #I number of variables
+
+int i, j, k
+double den, hold
+
+begin
+ do k = 1, n {
+
+ den = a[k,k]
+ if (den == 0.0d0) { # look for non-zero switch
+ do j = k + 1, n {
+ if (a[k,k] != 0.0d0) {
+ do i = k, n + 1 {
+ hold = a[i,j]
+ a[i,j] = a[i,k]
+ a[i,k] = hold
+ }
+ den = a[k,k]
+ }
+ }
+ if (den == 0.0d0) # if still zero, skip
+ next
+ }
+
+ do i = k, n + 1
+ a[i,k] = a[i,k] / den
+ do j = 1, n {
+ if (j != k) {
+ den = a[k,j]
+ do i = k, n + 1
+ a[i,j] = a[i,j] - a[i,k] * den
+ }
+ }
+ }
+end
+
+
+# RG_PNORMFILT -- Filter out any values greater than the normalization
+# from the kernel fft.
+
+procedure rg_pnormfilt (fft, nxfft, nyfft, norm)
+
+real fft[ARB] #I/O the input fft
+int nxfft #I the x length of the fft
+int nyfft #I the y length of the fft
+real norm #I the normalization factor
+
+int j, i_index
+
+begin
+ do j = 1, nyfft {
+ i_index = 1 + 2 * (j - 1) * nxfft
+ call rg_pnreplace (fft[i_index], nxfft, norm)
+ }
+end
+
+
+# RG_PFOURIER -- Compute the fourier spectrum of the convolution kernel.
+
+procedure rg_pfourier (fft, psfft, nxfft, nyfft)
+
+real fft[ARB] # the input fft
+real psfft[ARB] # fourier spectrum of the fft
+int nxfft # the x dimension of the fft
+int nyfft # the y dimension of the fft
+
+int j, i_index, o_index
+
+begin
+ do j = 1, nyfft {
+ i_index = 1 + 2 * (j - 1) * nxfft
+ o_index = 1 + (j - 1) * nxfft
+ call rg_pvfourier (fft[i_index], psfft[o_index], nxfft)
+ }
+end
+
+
+# RG_PVFOURIER -- Procedure to compute the fourier spectrum of a vector.
+
+procedure rg_pvfourier (a, b, nxfft)
+
+real a[ARB] # input vector in complex storage order
+real b[ARB] # output vector in real storage order
+int nxfft # length of vector
+
+int i
+
+begin
+ do i = 1, nxfft
+ b[i] = sqrt (a[2*i-1] ** 2 + a[2*i] ** 2)
+end
+
+
+# RG_PNREPLACE -- Replace values whose absolute value is greater than the
+# flux ratio.
+
+procedure rg_pnreplace (a, nxfft, norm)
+
+real a[ARB] #I/O ithe nput vector in complex storage order
+int nxfft #I the length of the vector
+real norm #I the flux ratio
+
+int i
+real val
+
+begin
+ do i = 1, 2 * nxfft, 2 {
+ val = sqrt (a[i] ** 2 + a[i+1] ** 2)
+ if (val > norm) {
+ a[i] = a[i] / val * norm
+ a[i+1] = a[i+1] / val * norm
+ }
+ }
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgpisfm.x b/pkg/images/immatch/src/psfmatch/rgpisfm.x
new file mode 100644
index 00000000..24df8fd7
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpisfm.x
@@ -0,0 +1,556 @@
+include <imhdr.h>
+include <ctype.h>
+include <gset.h>
+include "psfmatch.h"
+
+define HELPFILE "immatch$src/psfmatch/psfmatch.key"
+
+# Define the plot functions
+
+define PM_PPOWER 1
+define PM_PKERNEL 2
+
+# Define the plot types
+
+define PM_PCONTOUR 1
+define PM_PLINE 2
+define PM_PCOL 3
+
+# RG_PISFM -- Procedure to compute the shifts interactively.
+
+int procedure rg_pisfm (pm, imr, reglist, impsf, im1, imk, imp, im2, gd, id)
+
+pointer pm #I pointer to the psfmatch structure
+pointer imr #I/O pointer to the reference image/psf
+pointer reglist #I/O pointer to the regions list
+pointer impsf #I/O pointer to the input psf
+pointer im1 #I/O pointer to the input image
+pointer imp #I/O pointer to the fourier spectrum image
+pointer imk #I/O pointer to the kernel image
+pointer im2 #I/O pointer to the output image
+pointer gd #I graphics stream pointer
+pointer id #I display stream pointer
+
+int newref, newimage, newfourier, newfilter, plotfunc, plottype, wcs, key
+int newplot, ncolr, nliner, ip
+pointer sp, cmd
+real wx, wy
+int rg_pstati(), rg_psfm(), clgcur(), rg_pgqverify(), rg_pgtverify()
+int ctoi(), rg_pregions()
+pointer rg_pstatp()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ newref = YES
+ newimage = YES
+ newfourier = YES
+ newfilter = YES
+ ncolr = INDEFI
+ nliner = INDEFI
+ plotfunc = PM_PKERNEL
+ plottype = PM_PCONTOUR
+
+ # Compute the convolution kernel for the current image.
+ if (rg_pstati (pm, CONVOLUTION) == PM_CONIMAGE && rg_pstati (pm,
+ NREGIONS) <= 0) {
+ call gclear (gd)
+ call gflush (gd)
+ call printf ("The objects list is empty\n")
+ } else {
+ if (rg_psfm (pm, imr, im1, impsf, imk, newref) == OK) {
+ call rg_pplot (gd, pm, ncolr, nliner, plotfunc, plottype)
+ newref = NO
+ newimage = NO
+ newfourier = NO
+ newfilter = NO
+ } else {
+ call gclear (gd)
+ call gflush (gd)
+ call rg_pstats (pm, IMAGE, Memc[cmd], SZ_FNAME)
+ call printf ("Error computing kernel for image %s\n")
+ call pargstr (Memc[cmd])
+ }
+ }
+ newplot = NO
+
+ # Loop over the cursor commands.
+ while (clgcur ("gcommands", wx, wy, wcs, key, Memc[cmd], SZ_LINE) !=
+ EOF) {
+
+ switch (key) {
+
+ # Print the help page.
+ case '?':
+ call gpagefile (gd, HELPFILE, "")
+
+ # Quit the task gracefully.
+ case 'q':
+ if (rg_pgqverify ("psfmatch", pm, imk, key) == YES) {
+ call sfree (sp)
+ return (rg_pgtverify (key))
+ }
+
+ # Process colon commands.
+ case ':':
+ for (ip = 1; IS_WHITE(Memc[cmd+ip-1]); ip = ip + 1)
+ ;
+ switch (Memc[cmd+ip-1]) {
+
+ case 'x':
+ if (Memc[cmd+ip] != EOS && Memc[cmd+ip] != ' ') {
+ call rg_pcolon (gd, pm, imr, reglist, impsf, im1, imk,
+ NULL, im2, Memc[cmd], newref, newimage,
+ newfourier, newfilter)
+ } else {
+ ip = ip + 1
+ if (ctoi (Memc[cmd], ip, ncolr) <= 0) {
+ switch (plotfunc) {
+ case PM_PPOWER:
+ ncolr = rg_pstati (pm, NXFFT) / 2 + 1
+ case PM_PKERNEL:
+ ncolr = rg_pstati (pm, KNX) / 2 + 1
+ default:
+ ncolr = rg_pstati (pm, KNX) / 2 + 1
+ }
+ }
+ plottype = PM_PCOL
+ newplot = YES
+ }
+
+ case 'y':
+ if (Memc[cmd+ip] != EOS && Memc[cmd+ip] != ' ') {
+ call rg_pcolon (gd, pm, imr, reglist, impsf, im1, imk,
+ NULL, im2, Memc[cmd], newref, newimage,
+ newfourier, newfilter)
+ } else {
+ ip = ip + 1
+ if (ctoi (Memc[cmd], ip, nliner) <= 0) {
+ switch (plotfunc) {
+ case PM_PPOWER:
+ nliner = rg_pstati (pm, NYFFT) / 2 + 1
+ case PM_PKERNEL:
+ nliner = rg_pstati (pm, KNY) / 2 + 1
+ default:
+ nliner = rg_pstati (pm, KNY) / 2 + 1
+ }
+ }
+ plottype = PM_PLINE
+ newplot = YES
+ }
+
+
+ default:
+ call rg_pcolon (gd, pm, imr, reglist, impsf, im1, imk, NULL,
+ im2, Memc[cmd], newref, newimage, newfourier,
+ newfilter)
+ }
+
+ # Write the parameters to the parameter file.
+ case 'w':
+ call rg_pppars (pm)
+
+ # Recompute the convolution kernel function.
+ case 'f':
+
+ if (rg_pstati(pm,CONVOLUTION) == PM_CONIMAGE) {
+ if (newref == YES)
+ if (rg_pregions (reglist, imr, pm, 1, YES) > 0)
+ ;
+ else if (newimage == YES)
+ call rg_pindefr (pm)
+ }
+
+ if (rg_pstati (pm, NREGIONS) > 0 || rg_pstati (pm,
+ CONVOLUTION) != PM_CONIMAGE) {
+
+ if (newfourier == YES) {
+ call printf (
+ "\nRecomputing convolution kernel ...\n")
+ if (rg_psfm (pm, imr, im1, impsf, imk,
+ newref) == OK) {
+ ncolr = INDEFI
+ nliner = INDEFI
+ call rg_pplot (gd, pm, ncolr, nliner, plotfunc,
+ plottype)
+ newref = NO
+ newimage = NO
+ newfourier = NO
+ newfilter = NO
+ newplot = NO
+ } else
+ call printf (
+ "\nError computing new kernel ...\n")
+ }
+
+ if (newfilter == YES) {
+ if (Memr[rg_pstatp(pm,FFT)] != NULL) {
+ call rg_pfilter (pm)
+ ncolr = INDEFI
+ nliner = INDEFI
+ call rg_pplot (gd, pm, ncolr, nliner, plotfunc,
+ plottype)
+ newfilter = NO
+ newplot = NO
+ } else
+ call printf (
+ "The kernel fourier spectrum is undefined\n")
+ }
+
+ } else
+ call printf ("The objects list is empty\n")
+
+ # Draw a contour plot of the kernel.
+ case 'k':
+ if (plotfunc != PM_PKERNEL)
+ newplot = YES
+ if (plottype != PM_PCONTOUR)
+ newplot = YES
+ plotfunc = PM_PKERNEL
+ plottype = PM_PCONTOUR
+ ncolr = (1 + rg_pstati (pm, KNX)) / 2
+ nliner = (1 + rg_pstati (pm, KNY)) / 2
+
+ # Draw a contour plot of the fourier spectrum.
+ case 'p':
+ if (plotfunc != PM_PPOWER)
+ newplot = YES
+ if (plottype != PM_PCONTOUR)
+ newplot = YES
+ plotfunc = PM_PPOWER
+ plottype = PM_PCONTOUR
+ ncolr = (1 + rg_pstati (pm, NXFFT)) / 2
+ nliner = (1 + rg_pstati (pm, NYFFT)) / 2
+
+ # Plot a line of the current plot.
+ case 'x':
+ if (plottype != PM_PCOL)
+ newplot = YES
+ if (plottype == PM_PCONTOUR) {
+ ncolr = nint (wx)
+ nliner = nint (wy)
+ } else if (plottype == PM_PLINE) {
+ ncolr = nint (wx)
+ }
+ plottype = PM_PCOL
+
+ # Plot a line of the current plot.
+ case 'y':
+ if (plottype != PM_PLINE)
+ newplot = YES
+ if (plottype == PM_PCONTOUR) {
+ ncolr = nint (wx)
+ nliner = nint (wy)
+ } else if (plottype == PM_PCOL) {
+ ncolr = nint (wx)
+ }
+ plottype = PM_PLINE
+
+ # Redraw the current plot.
+ case 'r':
+ newplot = YES
+
+ # Do nothing gracefully.
+ default:
+ ;
+
+ }
+
+ if (newplot == YES) {
+ if (rg_pstati (pm, CONVOLUTION) == PM_CONIMAGE &&
+ rg_pstati (pm, NREGIONS) <= 0) {
+ call printf ("Warning: The objects list is empty\n")
+ } else if (newref == YES || newimage == YES ||
+ newfourier == YES || newfilter == YES) {
+ call printf (
+ "Warning: Convolution kernel should be refit\n")
+ } else if (rg_pstatp (pm, CONV) != NULL) {
+ call rg_pplot (gd, pm, ncolr, nliner, plotfunc, plottype)
+ newplot = NO
+ } else {
+ call printf (
+ "Warning: The convolution kernel is undefined\n")
+ }
+ }
+
+ }
+
+ call sfree (sp)
+end
+
+
+define QUERY "[Hit return to continue, n next image, q quit, w quit and update parameters]"
+
+# RG_PGQVERIFY -- Print a message in the status line asking the user if they
+# really want to quit, returning YES if they really want to quit, NO otherwise.
+
+int procedure rg_pgqverify (task, pm, imk, ch)
+
+char task[ARB] # task name
+pointer pm # pointer to psfmatch structure
+pointer imk # pointer to kernel image
+int ch # character keystroke command
+
+int wcs, stat
+pointer sp, cmd
+real wx, wy
+bool streq()
+int clgcur(), rg_pstati()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Print the status line query in reverse video and get the keystroke.
+ call printf (QUERY)
+ if (clgcur ("gcommands", wx, wy, wcs, ch, Memc[cmd], SZ_LINE) == EOF)
+ ;
+
+ # Process the command.
+ if (ch == 'q') {
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL)
+ call rg_pwrite (pm, imk, NULL)
+ stat = YES
+ } else if (ch == 'w') {
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL)
+ call rg_pwrite (pm, imk, NULL)
+ if (streq ("psfmatch", task))
+ call rg_pppars (pm)
+ stat = YES
+ } else if (ch == 'n') {
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL)
+ call rg_pwrite (pm, imk, NULL)
+ stat = YES
+ } else {
+ stat = NO
+ }
+
+ call sfree (sp)
+
+ return (stat)
+end
+
+
+# RG_PGTVERIFY -- Verify whether or not the user truly wishes to quit the
+# task.
+
+int procedure rg_pgtverify (ch)
+
+int ch #I the input keystroke command
+
+begin
+ if (ch == 'q') {
+ return (YES)
+ } else if (ch == 'w') {
+ return (YES)
+ } else if (ch == 'n') {
+ return (NO)
+ } else {
+ return (NO)
+ }
+end
+
+
+# RG_PPLOT -- Draw the default plot of the kernel fourier spectrum or the
+# kernel itself.
+
+procedure rg_pplot (gd, pm, col, line, plotfunc, plottype)
+
+pointer gd #I pointer to the graphics stream
+pointer pm #I pointer to the psfmatch structure
+int col #I column of cross-correlation function to plot
+int line #I line of cross-correlation function to plot
+int plotfunc #I the default plot function type
+int plottype #I the default plot type
+
+int nx, ny
+pointer sp, title, str, data
+int rg_pstati(), strlen()
+pointer rg_pstatp()
+
+begin
+ if (gd == NULL)
+ return
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (title, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Initialize the plot title and data.
+ switch (plotfunc) {
+ case PM_PPOWER:
+ call sprintf (Memc[title], SZ_LINE,
+ "Fourier Spectrum for Reference: %s Image: %s")
+ call rg_pstats (pm, REFIMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call rg_pstats (pm, IMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ data = rg_pstatp (pm, ASFFT)
+ nx = rg_pstati (pm, NXFFT)
+ ny = rg_pstati (pm, NYFFT)
+ case PM_PKERNEL:
+ call sprintf (Memc[title], SZ_LINE,
+ "Convolution Kernel for Reference: %s Image: %s")
+ call rg_pstats (pm, REFIMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call rg_pstats (pm, IMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ data = rg_pstatp (pm, CONV)
+ nx = rg_pstati (pm, KNX)
+ ny = rg_pstati (pm, KNY)
+ default:
+ call sprintf (Memc[title], SZ_LINE,
+ "Convolution Kernel for Reference: %s Image: %s")
+ call rg_pstats (pm, REFIMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call rg_pstats (pm, IMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ data = rg_pstatp (pm, CONV)
+ nx = rg_pstati (pm, KNX)
+ nx = rg_pstati (pm, KNY)
+ }
+ if (IS_INDEFI(col))
+ col = 1 + nx / 2
+ if (IS_INDEFI(line))
+ line = 1 + ny / 2
+
+ # Draw the plot.
+ if (ny == 1) {
+ switch (plotfunc) {
+ case PM_PPOWER:
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nLine %d")
+ call pargi (1)
+ call rg_pcpline (gd, Memc[title], Memr[rg_pstatp(pm,ASFFT)],
+ nx, ny, 1)
+ case PM_PKERNEL:
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nLine %d")
+ call pargi (1)
+ call rg_pcpline (gd, Memc[title], Memr[rg_pstatp(pm,CONV)],
+ nx, ny, 1)
+ default:
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nLine %d")
+ call pargi (1)
+ call rg_pcpline (gd, Memc[title], Memr[rg_pstatp(pm,CONV)],
+ nx, ny, 1)
+ }
+ } else {
+ switch (plottype) {
+ case PM_PCONTOUR:
+ call rg_contour (gd, Memc[title], "", Memr[data], nx, ny)
+ case PM_PLINE:
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nLine %d")
+ call pargi (line)
+ call rg_pcpline (gd, Memc[title], Memr[data], nx, ny, line)
+ case PM_PCOL:
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nColumn %d")
+ call pargi (col)
+ call rg_pcpcol (gd, Memc[title], Memr[data], nx, ny, col)
+ default:
+ call rg_contour (gd, Memc[title], "", Memr[data], nx, ny)
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_PCPLINE -- Plot a line of a 2D function.
+
+procedure rg_pcpline (gd, title, data, nx, ny, nline)
+
+pointer gd #I pointer to the graphics stream
+char title[ARB] #I title for the plot
+real data[nx,ARB] #I the input data array
+int nx, ny #I dimensions of the input data array
+int nline #I the line number
+
+int i
+pointer sp, str, x
+real ymin, ymax
+
+begin
+ # Return if no graphics stream.
+ if (gd == NULL)
+ return
+
+ # Check for valid line number.
+ if (nline < 1 || nline > ny)
+ return
+
+ # Allocate some working space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (x, nx, TY_REAL)
+
+ # Initialize the data.
+ do i = 1, nx
+ Memr[x+i-1] = i
+ call alimr (data[1,nline], nx, ymin, ymax)
+
+ # Set up the labels and the axes.
+ call gclear (gd)
+ call gswind (gd, 1.0, real (nx), ymin, ymax)
+ call glabax (gd, title, "X Lag", "X-Correlation Function")
+
+ # Plot the line profile.
+ call gseti (gd, G_PLTYPE, GL_SOLID)
+ call gpline (gd, Memr[x], data[1,nline], nx)
+ call gflush (gd)
+
+ call sfree (sp)
+end
+
+
+# RG_PCPCOL -- Plot a column of the cross-correlation function.
+
+procedure rg_pcpcol (gd, title, data, nx, ny, ncol)
+
+pointer gd #I pointer to the graphics stream
+char title[ARB] #I title of the column plot
+real data[nx,ARB] #I the input data array
+int nx, ny #I the dimensions of the input data array
+int ncol #I line number
+
+int i
+pointer sp, x, y
+real ymin, ymax
+
+begin
+ # Return if no graphics stream.
+ if (gd == NULL)
+ return
+
+ # Check for valid column number.
+ if (ncol < 1 || ncol > nx)
+ return
+
+ # Initialize.
+ call smark (sp)
+ call salloc (x, ny, TY_REAL)
+ call salloc (y, ny, TY_REAL)
+
+ # Get the data to be plotted.
+ do i = 1, ny {
+ Memr[x+i-1] = i
+ Memr[y+i-1] = data[ncol,i]
+ }
+ call alimr (Memr[y], ny, ymin, ymax)
+
+ # Set up the labels and the axes.
+ call gclear (gd)
+ call gswind (gd, 1.0, real (ny), ymin, ymax)
+ call glabax (gd, title, "Y Lag", "X-Correlation Function")
+
+ # Plot the profile.
+ call gseti (gd, G_PLTYPE, GL_SOLID)
+ call gpline (gd, Memr[x], Memr[y], ny)
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgppars.x b/pkg/images/immatch/src/psfmatch/rgppars.x
new file mode 100644
index 00000000..c8d49baa
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgppars.x
@@ -0,0 +1,124 @@
+include "psfmatch.h"
+
+# RG_PGPARS -- Read in the psf matching algorithm parameters.
+
+procedure rg_pgpars (pm)
+
+pointer pm #I pointer to psfmatch structure
+
+int ival
+pointer sp, str
+bool clgetb()
+int clgwrd(), clgeti(), btoi()
+real clgetr()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Initialize the psf matching structure.
+ call rg_pinit (pm, clgwrd ("convolution", Memc[str], SZ_LINE,
+ PM_CTYPES))
+
+ # Define the data and kernel sizes.
+ ival = clgeti ("dnx")
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, DNX, ival)
+ ival = clgeti ("dny")
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, DNY, ival)
+ ival = clgeti ("pnx")
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, PNX, ival)
+ ival = clgeti ("pny")
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_pseti (pm, PNY, ival)
+
+ # Centering parameters.
+ call rg_pseti (pm, CENTER, btoi (clgetb ("center")))
+
+ # Background value computation.
+ call clgstr ("background", Memc[str], SZ_LINE)
+ call rg_psets (pm, BSTRING, Memc[str])
+ call rg_psetr (pm, LOREJECT, clgetr ("loreject"))
+ call rg_psetr (pm, HIREJECT, clgetr ("hireject"))
+ call rg_psetr (pm, APODIZE, clgetr ("apodize"))
+
+ # Filtering parameters.
+ call rg_psetr (pm, UFLUXRATIO, clgetr ("fluxratio"))
+ call clgstr ("filter", Memc[str], SZ_LINE)
+ call rg_psets (pm, FSTRING, Memc[str])
+ call rg_psetr (pm, SXINNER, clgetr ("sx1"))
+ call rg_psetr (pm, SXOUTER, clgetr ("sx2"))
+ call rg_psetr (pm, SYINNER, clgetr ("sy1"))
+ call rg_psetr (pm, SYOUTER, clgetr ("sy2"))
+ call rg_pseti (pm, RADSYM, btoi (clgetb ("radsym")))
+ call rg_psetr (pm, THRESHOLD, (clgetr ("threshold")))
+
+ # Normalization parameter.
+ call rg_psetr (pm, NORMFACTOR, clgetr ("normfactor"))
+
+ #call rg_psetr (pm, PRATIO, clgetr ("pratio"))
+
+ call sfree (sp)
+end
+
+
+# RG_PPPARS -- Put the parameters required for the psf matching from
+# the cl to the parameter file.
+
+procedure rg_pppars (pm)
+
+pointer pm #I pointer to the psf matching structure
+
+pointer sp, str
+bool itob()
+int rg_pstati()
+real rg_pstatr()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Store the psf data string.
+ call rg_pstats (pm, PSFDATA, Memc[str], SZ_LINE)
+ call clpstr ("psf", Memc[str])
+
+ # Store the size parameters.
+ call clputi ("dnx", rg_pstati (pm, DNX))
+ call clputi ("dny", rg_pstati (pm, DNY))
+ call clputi ("pnx", rg_pstati (pm, PNX))
+ call clputi ("pny", rg_pstati (pm, PNY))
+
+ # Store the centering parameters.
+ call clputb ("center", itob (rg_pstati (pm, CENTER)))
+
+ # Store the background fitting parameters.
+ call rg_pstats (pm, BSTRING, Memc[str], SZ_LINE)
+ call clpstr ("background", Memc[str])
+ call clputr ("loreject", rg_pstatr (pm, LOREJECT))
+ call clputr ("hireject", rg_pstatr (pm, HIREJECT))
+ call clputr ("apodize", rg_pstatr (pm, APODIZE))
+
+ # Store the filtering parameters.
+ call clputr ("fluxratio", rg_pstatr(pm, UFLUXRATIO))
+ call rg_pstats (pm, FSTRING, Memc[str], SZ_LINE)
+ call clpstr ("filter", Memc[str])
+ call clputr ("sx1", rg_pstatr (pm, SXINNER))
+ call clputr ("sx2", rg_pstatr (pm, SXOUTER))
+ call clputr ("sy1", rg_pstatr (pm, SYINNER))
+ call clputr ("sy2", rg_pstatr (pm, SYOUTER))
+ call clputb ("radsym", itob (rg_pstati (pm, RADSYM)))
+ call clputr ("threshold", rg_pstatr (pm, THRESHOLD))
+
+ # Store the normalization parameters.
+ call clputr ("normfactor", rg_pstatr (pm, NORMFACTOR))
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgpregions.x b/pkg/images/immatch/src/psfmatch/rgpregions.x
new file mode 100644
index 00000000..c04dcf97
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpregions.x
@@ -0,0 +1,464 @@
+include <fset.h>
+include <imhdr.h>
+include "psfmatch.h"
+
+# RG_PREGIONS -- Decoode the regions specification. If the sections
+# string is NULL then a default region dnx by dny pixels wide centered
+# on the reference image is used. Otherwise the section centers are
+# read from the regions string or from the objects list.
+
+int procedure rg_pregions (list, im, pm, rp, reread)
+
+int list #I pointer to regions file list
+pointer im #I pointer to the image
+pointer pm #I pointer to the psfmatch structure
+int rp #I region pointer
+int reread #I reread the current file
+
+char fname[SZ_FNAME]
+int nregions, fd
+int open(), rg_prregions(), rg_pgregions(), fntgfnb()
+int rg_pstati()
+data fname[1] /EOS/
+errchk open(), fntgfnb(), close()
+
+begin
+ if (rp < 1 || rp > MAX_NREGIONS) {
+ nregions = 0
+ } else if (rg_pgregions (im, pm, rp, MAX_NREGIONS) > 0) {
+ nregions = rg_pstati (pm, NREGIONS)
+ } else if (list != NULL) {
+ if (reread == NO) {
+ iferr {
+ if (fntgfnb (list, fname, SZ_FNAME) != EOF) {
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+ nregions= rg_prregions (fd, im, pm, rp, MAX_NREGIONS)
+ call close (fd)
+ }
+ } then
+ nregions = 0
+ } else if (fname[1] != EOS) {
+ iferr {
+ fd = open (fname, READ_ONLY, TEXT_FILE)
+ nregions= rg_prregions (fd, im, pm, rp, MAX_NREGIONS)
+ call close (fd)
+ } then
+ nregions = 0
+ }
+ } else
+ nregions = 0
+
+ return (nregions)
+end
+
+
+# RG_PMKREGIONS -- Create a list of psf objects by selecting objects with
+# the image display cursor.
+
+int procedure rg_pmkregions (fd, im, pm, rp, max_nregions)
+
+int fd #I the output coordinates file descriptor
+pointer im #I pointer to the image
+pointer pm #I pointer to the psf matching structure
+int rp #I pointer to current region
+int max_nregions #I maximum number of regions
+
+int nregions, wcs, key, x1, x2, y1, y2
+pointer sp, region, cmd
+real x, y, xc, yc
+int clgcur(), rg_pstati()
+pointer rg_pstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_FNAME, TY_CHAR)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_prealloc (pm, max_nregions)
+
+ nregions = min (rp-1, rg_pstati (pm, NREGIONS))
+ while (nregions < max_nregions) {
+
+ # Identify the object.
+ call printf ("Mark object %d [any key=mark,q=quit]:\n")
+ call pargi (nregions + 1)
+ if (clgcur ("icommands", x, y, wcs, key, Memc[cmd], SZ_LINE) == EOF)
+ break
+ if (key == 'q')
+ break
+
+ # Center the object.
+ if (rg_pstati (pm, CENTER) == YES) {
+ call rg_pcntr (im, x, y, max (rg_pstati(pm, PNX),
+ rg_pstati(pm, PNY)), xc, yc)
+ } else {
+ xc = x
+ yc = y
+ }
+
+ # Compute the data section.
+ x1 = xc - rg_pstati (pm, DNX) / 2
+ x2 = x1 + rg_pstati (pm, DNX) - 1
+ y1 = yc - rg_pstati (pm, DNY) / 2
+ y2 = y1 + rg_pstati (pm, DNY) - 1
+
+ # Make sure that the region is on the image.
+ if (x1 < 1 || x2 > IM_LEN(im,1) || y1 < 1 || y2 >
+ IM_LEN(im,2))
+ next
+
+ if (fd != NULL) {
+ call fprintf (fd, "%0.3f %0.3f\n")
+ call pargr (xc)
+ call pargr (yc)
+ }
+
+ Memi[rg_pstatp(pm,RC1)+nregions] = x1
+ Memi[rg_pstatp(pm,RC2)+nregions] = x2
+ Memi[rg_pstatp(pm,RL1)+nregions] = y1
+ Memi[rg_pstatp(pm,RL2)+nregions] = y2
+ Memr[rg_pstatp(pm,RZERO)+nregions] = INDEFR
+ Memr[rg_pstatp(pm,RXSLOPE)+nregions] = INDEFR
+ Memr[rg_pstatp(pm,RYSLOPE)+nregions] = INDEFR
+ nregions = nregions + 1
+
+ }
+
+ # Reallocate the correct amount of space.
+ call rg_pseti (pm, NREGIONS, nregions)
+ if (nregions > 0) {
+ call rg_prealloc (pm, nregions)
+ if (fd != NULL) {
+ call fstats (fd, F_FILENAME, Memc[region], SZ_FNAME)
+ call rg_psets (pm, PSFDATA, Memc[region])
+ } else
+ call rg_psets (pm, PSFDATA, "")
+ } else {
+ call rg_prfree (pm)
+ call rg_psets (pm, PSFDATA, "")
+ }
+
+ call sfree (sp)
+ return (nregions)
+end
+
+
+# RG_PRREGIONS -- Procedure to read the regions from a file.
+
+int procedure rg_prregions (fd, im, pm, rp, max_nregions)
+
+int fd #I regions file descriptor
+pointer im #I pointer to the image
+pointer pm #I pointer to psf matching structure
+int rp #I pointer to current region
+int max_nregions #I maximum number of regions
+
+int nregions, x1, y1, x2, y2
+pointer sp, line
+real x, y, xc, yc
+int rg_pstati(), getline()
+pointer rg_pstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (line, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_prealloc (pm, max_nregions)
+
+ # Decode the regions string.
+ nregions = min (rp - 1, rg_pstati (pm, NREGIONS))
+ while (getline (fd, Memc[line]) != EOF) {
+
+ if (nregions >= max_nregions)
+ break
+
+ call sscan (Memc[line])
+ call gargr (x)
+ call gargr (y)
+ if (rg_pstati (pm, CENTER) == YES) {
+ call rg_pcntr (im, x, y, max (rg_pstati(pm, PNX),
+ rg_pstati(pm, PNY)), xc, yc)
+ } else {
+ xc = x
+ yc = y
+ }
+
+ # Compute the data section.
+ x1 = xc - rg_pstati (pm, DNX) / 2
+ x2 = x1 + rg_pstati (pm, DNX) - 1
+ if (IM_NDIM(im) == 1) {
+ y1 = 1
+ y2 = 1
+ } else {
+ y1 = yc - rg_pstati (pm, DNY) / 2
+ y2 = y1 + rg_pstati (pm, DNY) - 1
+ }
+
+ # Make sure that the region is on the image.
+ if (x1 < 1 || x2 > IM_LEN(im,1) || y1 < 1 || y2 >
+ IM_LEN(im,2))
+ next
+
+ # Add the new region to the list.
+ Memi[rg_pstatp(pm,RC1)+nregions] = x1
+ Memi[rg_pstatp(pm,RC2)+nregions] = x2
+ Memi[rg_pstatp(pm,RL1)+nregions] = y1
+ Memi[rg_pstatp(pm,RL2)+nregions] = y2
+ Memr[rg_pstatp(pm,RZERO)+nregions] = INDEFR
+ Memr[rg_pstatp(pm,RXSLOPE)+nregions] = INDEFR
+ Memr[rg_pstatp(pm,RYSLOPE)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+
+ call rg_pseti (pm, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_prealloc (pm, nregions)
+ else
+ call rg_prfree (pm)
+
+ call sfree (sp)
+ return (nregions)
+end
+
+
+# RG_PGREGIONS -- Procedure to compute the column and line limits given
+# an x and y position and a default size.
+
+int procedure rg_pgregions (im, pm, rp, max_nregions)
+
+pointer im #I pointer to the image
+pointer pm #I pointer to psf matching structure
+int rp #I pointer to the current region
+int max_nregions #I maximum number of regions
+
+int ncols, nlines, nregions
+int x1, x2, y1, y2
+pointer sp, region
+real x, y, xc, yc
+int rg_pstati(), nscan()
+pointer rg_pstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information.
+ call rg_prealloc (pm, max_nregions)
+
+ # Get the constants.
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+
+ # Decode the center.
+ call rg_pstats (pm, PSFDATA, Memc[region], SZ_LINE)
+ nregions = min (rp - 1, rg_pstati (pm, NREGIONS))
+ call sscan (Memc[region])
+ call gargr (x)
+ call gargr (y)
+
+ # Compute the data region.
+ if (nscan() >= 2) {
+
+ # Compute a more accurate center.
+ if (rg_pstati (pm, CENTER) == YES) {
+ call rg_pcntr (im, x, y, max (rg_pstati(pm, PNX),
+ rg_pstati(pm, PNY)), xc, yc)
+ } else {
+ xc = x
+ yc = y
+ }
+
+ # Compute the data section.
+ x1 = xc - rg_pstati (pm, DNX) / 2
+ x2 = x1 + rg_pstati (pm, DNX) - 1
+ if (IM_NDIM(im) == 1) {
+ y1 = 1
+ y2 = 1
+ } else {
+ y1 = yc - rg_pstati (pm, DNY) / 2
+ y2 = y1 + rg_pstati (pm, DNY) - 1
+ }
+
+ # Make sure that the region is on the image.
+ if (x1 >= 1 && x2 <= IM_LEN(im,1) && y1 >= 1 &&
+ y2 <= IM_LEN(im,2)) {
+ Memi[rg_pstatp(pm,RC1)+nregions] = x1
+ Memi[rg_pstatp(pm,RC2)+nregions] = x2
+ Memi[rg_pstatp(pm,RL1)+nregions] = y1
+ Memi[rg_pstatp(pm,RL2)+nregions] = y2
+ Memr[rg_pstatp(pm,RZERO)+nregions] = INDEFR
+ Memr[rg_pstatp(pm,RXSLOPE)+nregions] = INDEFR
+ Memr[rg_pstatp(pm,RYSLOPE)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+ }
+
+
+ # Reallocate the correct amount of space.
+ call rg_pseti (pm, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_prealloc (pm, nregions)
+ else
+ call rg_prfree (pm)
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_PCNTR -- Compute star center using MPC algorithm.
+
+procedure rg_pcntr (im, xstart, ystart, boxsize, xcntr, ycntr)
+
+pointer im #I pointer to the input image
+real xstart, ystart #I initial position
+int boxsize #I width of the centering box
+real xcntr, ycntr #O computed center
+
+int x1, x2, y1, y2, half_box
+int ncols, nrows, nx, ny, try
+real xinit, yinit
+pointer bufptr, sp, x_vect, y_vect
+int imgs2r()
+
+begin
+ # Inialize.
+ half_box = (boxsize - 1) / 2
+ xinit = xstart
+ ncols = IM_LEN (im, 1)
+ if (IM_NDIM(im) == 1) {
+ yinit = 1
+ nrows = 1
+ } else {
+ yinit = ystart
+ nrows = IM_LEN (im, 2)
+ }
+ try = 0
+
+ # Iterate until pixel shifts are less than one.
+ repeat {
+
+ # Define region to extract.
+ x1 = max (xinit - half_box, 1.0) +0.5
+ x2 = min (xinit + half_box, real(ncols)) +0.5
+ y1 = max (yinit - half_box, 1.0) +0.5
+ y2 = min (yinit + half_box, real(nrows)) +0.5
+ nx = x2 - x1 + 1
+ ny = y2 - y1 + 1
+
+ # Extract region around center
+ bufptr = imgs2r (im, x1, x2, y1, y2)
+
+ # Compute the new center.
+ call smark (sp)
+ if (IM_NDIM(im) == 1) {
+ call salloc (x_vect, nx, TY_REAL)
+ call aclrr (Memr[x_vect], nx)
+ call rg_prowsum (Memr[bufptr], Memr[x_vect], nx, ny)
+ call rg_pcenter (Memr[x_vect], nx, xcntr)
+ ycntr = 1
+ } else {
+ call salloc (x_vect, nx, TY_REAL)
+ call salloc (y_vect, ny, TY_REAL)
+ call aclrr (Memr[x_vect], nx)
+ call aclrr (Memr[y_vect], ny)
+ call rg_prowsum (Memr[bufptr], Memr[x_vect], nx, ny)
+ call rg_pcolsum (Memr[bufptr], Memr[y_vect], nx, ny)
+ call rg_pcenter (Memr[x_vect], nx, xcntr)
+ call rg_pcenter (Memr[y_vect], ny, ycntr)
+ }
+ call sfree (sp)
+
+ # Check for INDEF centers.
+ if (IS_INDEFR(xcntr) || IS_INDEFR(ycntr)) {
+ xcntr = xinit
+ ycntr = yinit
+ break
+ }
+
+ # Add in offsets
+ xcntr = xcntr + x1
+ ycntr = ycntr + y1
+
+ try = try + 1
+ if (try == 1) {
+ if ((abs(xcntr-xinit) > 1.0) || (abs(ycntr-yinit) > 1.0)) {
+ xinit = xcntr
+ yinit = ycntr
+ }
+ } else
+ break
+ }
+end
+
+
+# RG_PROWSUM -- Sum all rows in a raster.
+
+procedure rg_prowsum (v, row, nx, ny)
+
+real v[nx,ny] #I the input subraster
+real row[ARB] #O the output row sum
+int nx, ny #I the dimensions of the subraster
+
+int i, j
+
+begin
+ do i = 1, ny
+ do j = 1, nx
+ row[j] = row[j] + v[j,i]
+end
+
+
+# RG_PCOLSUM -- Sum all columns in a raster.
+
+procedure rg_pcolsum (v, col, nx, ny)
+
+real v[nx,ny] #I the input subraster
+real col[ARB] #O the output column sum
+int nx, ny #I the dimensions of the subraster
+
+int i, j
+
+begin
+ do i = 1, ny
+ do j = 1, nx
+ col[j] = col[j] + v[i,j]
+end
+
+
+# RG_PCENTER -- Compute center of gravity of array.
+
+procedure rg_pcenter (v, nv, vc)
+
+real v[ARB] #I the input vector
+int nv #I the length of the vector
+real vc #O the output center
+
+int i
+real sum1, sum2, sigma, cont
+
+begin
+ # Compute first moment
+ sum1 = 0.0
+ sum2 = 0.0
+
+ call aavgr (v, nv, cont, sigma)
+
+ do i = 1, nv
+ if (v[i] > cont) {
+ sum1 = sum1 + (i-1) * (v[i] - cont)
+ sum2 = sum2 + (v[i] - cont)
+ }
+
+ # Determine center
+ if (sum2 == 0.0)
+ vc = INDEFR
+ else
+ vc = sum1 / sum2
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgpsfm.x b/pkg/images/immatch/src/psfmatch/rgpsfm.x
new file mode 100644
index 00000000..493d48c9
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpsfm.x
@@ -0,0 +1,815 @@
+include <imhdr.h>
+include <math/gsurfit.h>
+include "psfmatch.h"
+
+# RG_PSFM -- Procedure to match the psf functions of two images.
+
+int procedure rg_psfm (pm, imr, im1, impsf, imk, newref)
+
+pointer pm #I pointer to psf matching structure
+pointer imr #I pointer to reference image
+pointer im1 #I pointer to input image
+pointer impsf #I pointer to the psf image
+pointer imk #I pointer to kernel image
+int newref #I new reference image ?
+
+int stat
+int rg_pstati(), rg_pfget(), rg_psfget(), rg_kget()
+pointer rg_pstatp()
+
+begin
+ # Compute the convolution kernel.
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL) {
+
+ # Compute the kernel using raw image data or the psf image.
+ if (rg_pstati (pm,CONVOLUTION) == PM_CONIMAGE) {
+
+ # Set the kernel size to the user specified kernel size.
+ call rg_pseti (pm, KNX, rg_pstati (pm, PNX))
+ if (IM_NDIM(imr) == 1)
+ call rg_pseti (pm, KNY, 1)
+ else
+ call rg_pseti (pm, KNY, rg_pstati (pm, PNY))
+
+ # Compute the FFTS of the input and reference image.
+ stat = rg_pfget (pm, imr, im1, newref)
+
+ } else {
+
+ # Set the kernel size to the psf image size
+ call rg_pseti (pm, KNX, IM_LEN (impsf,1))
+ if (IM_NDIM(imr) == 1)
+ call rg_pseti (pm, KNY, 1)
+ else
+ call rg_pseti (pm, KNY, IM_LEN(impsf,2))
+
+ # Compute the FFTS of the input and reference psf images.
+ stat = rg_psfget (pm, imr, impsf, newref)
+ }
+
+ # Delete working arrays if an error occurs.
+ if (stat == ERR) {
+ if (rg_pstatp (pm, REFFFT) != NULL)
+ call mfree (rg_pstatp (pm, REFFFT), TY_REAL)
+ call rg_psetp (pm, REFFFT, NULL)
+ if (rg_pstatp (pm, IMFFT) != NULL)
+ call mfree (rg_pstatp (pm, IMFFT), TY_REAL)
+ call rg_psetp (pm, IMFFT, NULL)
+ if (rg_pstatp (pm, FFT) != NULL)
+ call mfree (rg_pstatp (pm, FFT), TY_REAL)
+ call rg_psetp (pm, FFT, NULL)
+ if (rg_pstatp (pm, CONV) != NULL)
+ call mfree (rg_pstatp (pm, CONV), TY_REAL)
+ call rg_psetp (pm, CONV, NULL)
+ if (rg_pstatp (pm, ASFFT) != NULL)
+ call mfree (rg_pstatp (pm, ASFFT), TY_REAL)
+ call rg_psetp (pm, ASFFT, NULL)
+ }
+
+ # Do the filtering in frequency space.
+ if (rg_pstatp (pm, FFT) != NULL)
+ call rg_pfilter (pm)
+
+ } else {
+
+ # Set the kernel size.
+ call rg_pseti (pm, KNX, IM_LEN(imk,1))
+ if (IM_NDIM(im1) == 1)
+ call rg_pseti (pm, KNY, 1)
+ else
+ call rg_pseti (pm, KNY, IM_LEN(imk,2))
+
+ # Read in the convolution kernel.
+ stat = rg_kget (pm, imk)
+
+ # Delete working arrays if an error occurs.
+ if (stat == ERR) {
+ if (rg_pstatp (pm, REFFFT) != NULL)
+ call mfree (rg_pstatp (pm, REFFFT), TY_REAL)
+ call rg_psetp (pm, REFFFT, NULL)
+ if (rg_pstatp (pm, IMFFT) != NULL)
+ call mfree (rg_pstatp (pm, IMFFT), TY_REAL)
+ call rg_psetp (pm, IMFFT, NULL)
+ if (rg_pstatp (pm, FFT) != NULL)
+ call mfree (rg_pstatp (pm, FFT), TY_REAL)
+ call rg_psetp (pm, FFT, NULL)
+ if (rg_pstatp (pm, CONV) != NULL)
+ call mfree (rg_pstatp (pm, CONV), TY_REAL)
+ call rg_psetp (pm, CONV, NULL)
+ if (rg_pstatp (pm, ASFFT) != NULL)
+ call mfree (rg_pstatp (pm, ASFFT), TY_REAL)
+ call rg_psetp (pm, ASFFT, NULL)
+ }
+ }
+
+ return (stat)
+end
+
+
+# RG_PFGET -- Compute the psfmatching function using Fourier techniques.
+
+int procedure rg_pfget (pm, imr, im1, newref)
+
+pointer pm #I pointer to psfmatch structure
+pointer imr #I pointer to reference image
+pointer im1 #I pointer to input image
+int newref #I new reference image ?
+
+int i, nregions, nrimcols, nrimlines, nrcols, nrlines, nrpcols, nrplines
+int nborder, stat, rc1, rc2, rl1, rl2, nxfft, nyfft
+pointer sp, str, coeff, dim, rbuf, ibuf, rsum, isum, border
+pointer prc1, prc2, prl1, prl2, przero, prxslope, pryslope, reffft, imfft, fft
+real rwtsum, iwtsum, rscale, iscale, rnscale, inscale
+bool fp_equalr()
+int rg_pstati(), rg_border(), rg_szfft()
+pointer rg_pstatp(), rg_pgdata()
+real rg_pstatr(), rg_pnsum(), rg_pg1norm(), rg_pg2norm()
+real rg_pg10f(), rg_pg20f()
+
+define nextimage_ 11
+
+begin
+ # Assemble the PSF data by looping over the regions list.
+ nregions = rg_pstati (pm, NREGIONS)
+ if (nregions <= 0)
+ return (ERR)
+
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (coeff, max (GS_SAVECOEFF+6, 9), TY_REAL)
+ call salloc (dim, 2, TY_INT)
+
+ # Get the reference region pointers.
+ prc1 = rg_pstatp (pm, RC1)
+ prc2 = rg_pstatp (pm, RC2)
+ prl1 = rg_pstatp (pm, RL1)
+ prl2 = rg_pstatp (pm, RL2)
+ przero = rg_pstatp (pm, RZERO)
+ prxslope = rg_pstatp (pm, RXSLOPE)
+ pryslope = rg_pstatp (pm, RYSLOPE)
+
+ # Check to see if the reference / input images are 1D.
+ nrimcols = IM_LEN(imr,1)
+ nrpcols = rg_pstati (pm, PNX)
+ if (IM_NDIM(imr) == 1) {
+ nrimlines = 1
+ nrplines = 1
+ } else {
+ nrimlines = IM_LEN(imr,2)
+ nrplines = rg_pstati (pm, PNY)
+ }
+
+ # Initialize
+ rwtsum = 0.0
+ iwtsum = 0.0
+ rnscale = INDEFR
+ inscale = INDEFR
+ rbuf = NULL
+ ibuf = NULL
+ stat = OK
+ if (newref == YES)
+ call calloc (rsum, rg_pstati (pm, DNX) * rg_pstati (pm, DNY),
+ TY_REAL)
+ call calloc (isum, rg_pstati (pm, DNX) * rg_pstati (pm, DNY),
+ TY_REAL)
+
+ do i = 1, nregions {
+
+ # Get the reference subraster regions.
+ rc1 = max (1, min (nrimcols, Memi[prc1+i-1]))
+ rc2 = min (nrimcols, max (1, Memi[prc2+i-1]))
+ rl1 = max (1, min (nrimlines, Memi[prl1+i-1]))
+ rl2 = min (nrimlines, max (1, Memi[prl2+i-1]))
+ nrcols = rc2 - rc1 + 1
+ nrlines = rl2 - rl1 + 1
+
+ # Go to next object if reference region is off the image.
+ if (nrcols < rg_pstati (pm, DNX) || (IM_NDIM(imr) == 2 &&
+ nrlines < rg_pstati(pm, DNY))) {
+ call rg_pstats (pm, REFIMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Reference object %d: %s[%d:%d,%d:%d] is off image.\n")
+ call pargi (i)
+ call pargstr (Memc[str])
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (rl1)
+ call pargi (rl2)
+ next
+ }
+
+ if (newref == YES) {
+
+ # Get the reference data.
+ rbuf = rg_pgdata (imr, rc1, rc2, rl1, rl2)
+
+ # Do the reference image background subtraction.
+ border = NULL
+ nborder = rg_border (Memr[rbuf], nrcols, nrlines, nrpcols,
+ nrplines, border)
+ call rg_pscale (pm, Memr[border], nborder, nrcols,
+ nrlines, nrpcols, nrplines, rg_pstatr (pm, BVALUER),
+ Memr[coeff])
+ if (border != NULL)
+ call mfree (border, TY_REAL)
+
+ # Save the coefficients.
+ Memr[przero+i-1] = Memr[coeff]
+ Memr[prxslope+i-1] = Memr[coeff+1]
+ Memr[pryslope+i-1] = Memr[coeff+2]
+
+ # Subtract the reference background.
+ call rg_subtract (Memr[rbuf], nrcols, nrlines,
+ Memr[przero+i-1], Memr[prxslope+i-1], Memr[pryslope+i-1])
+
+ # Apodize the reference image data.
+ if (rg_pstatr (pm, APODIZE) > 0.0)
+ call rg_apodize (Memr[rbuf], nrcols, nrlines,
+ rg_pstatr (pm, APODIZE), YES)
+
+ # Compute the scale factors and accumulate the weighted sums.
+ rscale = rg_pnsum (Memr[rbuf], nrcols, nrlines, nrpcols,
+ nrplines)
+ if (! IS_INDEFR(rscale)) {
+ if (IS_INDEFR(rnscale))
+ rnscale = 1.0 / rscale
+ }
+ if (IS_INDEFR(rscale))
+ rscale = 1.0
+ else
+ rscale = rscale / rnscale
+
+ call amulkr (Memr[rbuf], rscale, Memr[rbuf], nrcols *
+ nrlines)
+ rwtsum = rwtsum + rscale
+ call aaddr (Memr[rsum], Memr[rbuf], Memr[rsum], nrcols *
+ nrlines)
+
+ call mfree (rbuf, TY_REAL)
+ }
+
+ # Get the input image data
+ ibuf = rg_pgdata (im1, rc1, rc2, rl1, rl2)
+
+ # Compute the zero point, and the x and y slopes of input image.
+ border = NULL
+ nborder = rg_border (Memr[ibuf], nrcols, nrlines, nrpcols,
+ nrplines, border)
+ call rg_pscale (pm, Memr[border], nborder, nrcols, nrlines,
+ nrpcols, nrplines, rg_pstatr (pm, BVALUE), Memr[coeff])
+ if (border != NULL)
+ call mfree (border, TY_REAL)
+
+ # Subtract the background from the input image.
+ call rg_subtract (Memr[ibuf], nrcols, nrlines, Memr[coeff],
+ Memr[coeff+1], Memr[coeff+2])
+
+ # Apodize the data.
+ if (rg_pstatr (pm, APODIZE) > 0.0)
+ call rg_apodize (Memr[ibuf], nrcols, nrlines, rg_pstatr (pm,
+ APODIZE), YES)
+
+ # Compute the scale factors and accumulate the weighted sums for
+ # input image.
+ iscale = rg_pnsum (Memr[ibuf], nrcols, nrlines, nrpcols, nrplines)
+ if (! IS_INDEFR(iscale)) {
+ if (IS_INDEFR(inscale))
+ inscale = 1.0 / iscale
+ }
+ if (IS_INDEFR(iscale))
+ iscale = 1.0
+ else
+ iscale = iscale / inscale
+
+ call amulkr (Memr[ibuf], iscale, Memr[ibuf], nrcols * nrlines)
+ iwtsum = iwtsum + iscale
+ call aaddr (Memr[isum], Memr[ibuf], Memr[isum], nrcols * nrlines)
+
+ # Free the individual image buffers.
+ call mfree (ibuf, TY_REAL)
+ }
+
+ # Check to see if any data was read.
+ if (iwtsum <= 0.0) {
+ stat = ERR
+ goto nextimage_
+ }
+
+ # Normalize the summed buffers by the weights.
+ if (newref == YES) {
+ if (! fp_equalr (rwtsum, 0.0))
+ call adivkr (Memr[rsum], rwtsum, Memr[rsum], nrcols * nrlines)
+ }
+ if (! fp_equalr (iwtsum, 0.0))
+ call adivkr (Memr[isum], iwtsum, Memr[isum], nrcols * nrlines)
+
+ # Figure out how big the Fourier transform has to be, given
+ # the size of the reference subraster, the window size and
+ # the fact that the FFT must be a power of 2.
+
+ nxfft = rg_szfft (nrcols, 0)
+ if (nrlines == 1)
+ nyfft = 1
+ else
+ nyfft = rg_szfft (nrlines, 0)
+ call rg_pseti (pm, NXFFT, nxfft)
+ call rg_pseti (pm, NYFFT, nyfft)
+
+ imfft = rg_pstatp (pm, IMFFT)
+ if (imfft != NULL)
+ call mfree (imfft, TY_REAL)
+ call calloc (imfft, 2 * nxfft * nyfft, TY_REAL)
+ call rg_psetp (pm, IMFFT, imfft)
+
+ # Allocate space for the fft.
+ fft = rg_pstatp (pm, FFT)
+ if (fft != NULL)
+ call mfree (fft, TY_REAL)
+ call calloc (fft, 2 * nxfft * nyfft, TY_REAL)
+ call rg_psetp (pm, FFT, fft)
+
+ # Allocate space for the reference and input image ffts
+ if (newref == YES) {
+
+ reffft = rg_pstatp (pm, REFFFT)
+ if (reffft != NULL)
+ call mfree (reffft, TY_REAL)
+ call calloc (reffft, 2 * nxfft * nyfft, TY_REAL)
+ call rg_psetp (pm, REFFFT, reffft)
+
+ # Load the reference image FFT.
+ call rg_rload (Memr[rsum], nrcols, nrlines, Memr[fft], nxfft,
+ nyfft)
+ call mfree (rsum, TY_REAL)
+ rsum = NULL
+
+ # Load the input image FFT.
+ call rg_iload (Memr[isum], nrcols, nrlines, Memr[fft], nxfft,
+ nyfft)
+ call mfree (isum, TY_REAL)
+ isum = NULL
+
+ # Shift the data for easy of filtering.
+ call rg_fshift (Memr[fft], Memr[fft], 2 * nxfft, nyfft)
+
+ # Compute the Fourier Transform of the reference and input image
+ # data.
+ Memi[dim] = nxfft
+ Memi[dim+1] = nyfft
+ if (Memi[dim+1] == 1)
+ call rg_fourn (Memr[fft], Memi[dim], 1, 1)
+ else
+ call rg_fourn (Memr[fft], Memi[dim], 2, 1)
+
+ # Compute the flux ratio between the two data sets.
+ if (IS_INDEFR(rg_pstatr(pm, UFLUXRATIO))) {
+ if (rg_pstati (pm, BACKGRD) == PM_BNONE)
+ call rg_psetr (pm, FLUXRATIO, rg_pg2norm (Memr[fft],
+ 2 * nxfft, nyfft))
+ else
+ call rg_psetr (pm, FLUXRATIO, rg_pg20f (Memr[fft],
+ 2 * nxfft, nyfft))
+ } else
+ call rg_psetr (pm, FLUXRATIO, rg_pstatr (pm, UFLUXRATIO))
+
+ # Separate the two transforms and compute the division.
+ call rg_pdivfft (Memr[fft], Memr[reffft], Memr[imfft], Memr[fft],
+ 2 * nxfft, nyfft)
+
+ } else {
+
+
+ # Get the reference image FFT.
+ reffft = rg_pstatp (pm, REFFFT)
+
+ # Load the input image FFT.
+ call rg_rload (Memr[isum], nrcols, nrlines, Memr[imfft], nxfft,
+ nyfft)
+ call mfree (isum, TY_REAL)
+ isum = NULL
+
+ # Shift the data for easy of filtering.
+ call rg_fshift (Memr[imfft], Memr[imfft], 2 * nxfft, nyfft)
+
+ # Compute the Fourier Transform of the input image data.
+ Memi[dim] = nxfft
+ Memi[dim+1] = nyfft
+ if (Memi[dim+1] == 1)
+ call rg_fourn (Memr[imfft], Memi[dim], 1, 1)
+ else
+ call rg_fourn (Memr[imfft], Memi[dim], 2, 1)
+
+ # Compute the flux ratio between the two data sets.
+ if (IS_INDEFR(rg_pstatr(pm, UFLUXRATIO))) {
+ if (rg_pstati (pm, BACKGRD) == PM_BNONE)
+ call rg_psetr (pm, FLUXRATIO, rg_pg1norm (Memr[reffft],
+ 2 * nxfft, nyfft) / rg_pg1norm (Memr[imfft], 2 * nxfft,
+ nyfft))
+ else
+ call rg_psetr (pm, FLUXRATIO, rg_pg10f (Memr[reffft],
+ 2 * nxfft, nyfft) / rg_pg10f (Memr[imfft], 2 * nxfft,
+ nyfft))
+ } else
+ call rg_psetr (pm, FLUXRATIO, rg_pstatr (pm, UFLUXRATIO))
+
+ # Divide the two functions.
+ call adivx (Memr[reffft], Memr[imfft], Memr[fft], nxfft * nyfft)
+ }
+
+ # Normalize the FFT.
+ call rg_pnorm (Memr[fft], nxfft, nyfft, rg_pstatr (pm, FLUXRATIO))
+
+
+nextimage_
+
+ if (rsum != NULL)
+ call mfree (rsum, TY_REAL)
+ if (isum != NULL)
+ call mfree (isum, TY_REAL)
+ call sfree (sp)
+ if (stat == ERR)
+ return (ERR)
+ else
+ return (OK)
+end
+
+
+# RG_PSFGET -- Compute the psfmatching function using Fourier techniques.
+
+int procedure rg_psfget (pm, imr, impsf, newref)
+
+pointer pm #I pointer to the psfmatch structure
+pointer imr #I pointer to the reference psf
+pointer impsf #I pointer to the input image psf
+int newref #I new reference image
+
+int nrcols, nrlines, nxfft, nyfft
+pointer sp, dim, rbuf, ibuf, imfft, fft, reffft
+int rg_szfft()
+pointer rg_pgdata(), rg_pstatp()
+real rg_pstatr(), rg_pg2norm(), rg_pg1norm()
+
+begin
+ call smark (sp)
+ call salloc (dim, 2, TY_INT)
+
+ nrcols = IM_LEN(imr,1)
+ if (IM_NDIM(imr) == 1)
+ nrlines = 1
+ else
+ nrlines = IM_LEN(imr,2)
+
+ # Get the psf data.
+ rbuf = NULL
+ ibuf = NULL
+ if (newref == YES) {
+ call calloc (rbuf, nrcols * nrlines, TY_REAL)
+ rbuf = rg_pgdata (imr, 1, nrcols, 1, nrlines)
+ }
+ call calloc (ibuf, nrcols * nrlines, TY_REAL)
+ ibuf = rg_pgdata (impsf, 1, nrcols, 1, nrlines)
+
+ # Compute the size for the FFT buffers.
+ nxfft = rg_szfft (nrcols, 0)
+ if (nrlines == 1)
+ nyfft = 1
+ else
+ nyfft = rg_szfft (nrlines, 0)
+ call rg_pseti (pm, NXFFT, nxfft)
+ call rg_pseti (pm, NYFFT, nyfft)
+
+ imfft = rg_pstatp (pm, IMFFT)
+ if (imfft != NULL)
+ call mfree (imfft, TY_REAL)
+ call calloc (imfft, 2 * nxfft * nyfft, TY_REAL)
+ call rg_psetp (pm, IMFFT, imfft)
+
+ # Allocate space for the fft.
+ fft = rg_pstatp (pm, FFT)
+ if (fft != NULL)
+ call mfree (fft, TY_REAL)
+ call calloc (fft, 2 * nxfft * nyfft, TY_REAL)
+ call rg_psetp (pm, FFT, fft)
+
+ if (newref == YES) {
+
+ reffft = rg_pstatp (pm, REFFFT)
+ if (reffft != NULL)
+ call mfree (reffft, TY_REAL)
+ call calloc (reffft, 2 * nxfft * nyfft, TY_REAL)
+ call rg_psetp (pm, REFFFT, reffft)
+
+ # Load the reference image FFT.
+ call rg_rload (Memr[rbuf], nrcols, nrlines, Memr[fft], nxfft,
+ nyfft)
+
+ # Load the input image FFT.
+ call rg_iload (Memr[ibuf], nrcols, nrlines, Memr[fft], nxfft,
+ nyfft)
+
+ # Shift the data for easy of filtering.
+ call rg_fshift (Memr[fft], Memr[fft], 2 * nxfft, nyfft)
+
+ # Compute the Fourier Transform of the reference and input image
+ # data.
+ Memi[dim] = nxfft
+ Memi[dim+1] = nyfft
+ if (Memi[dim+1] == 1)
+ call rg_fourn (Memr[fft], Memi[dim], 1, 1)
+ else
+ call rg_fourn (Memr[fft], Memi[dim], 2, 1)
+
+ # Compute the flux ratio between the two data sets.
+ if (IS_INDEFR(rg_pstatr(pm, UFLUXRATIO)))
+ call rg_psetr (pm, FLUXRATIO, rg_pg2norm (Memr[fft],
+ 2 * nxfft, nyfft))
+ else
+ call rg_psetr (pm, FLUXRATIO, rg_pstatr(pm, UFLUXRATIO))
+
+ # Separate the two transforms and compute the division.
+ call rg_pdivfft (Memr[fft], Memr[reffft], Memr[imfft], Memr[fft],
+ 2 * nxfft, nyfft)
+
+ } else {
+
+ # Get the reference image FFT.
+ reffft = rg_pstatp (pm, REFFFT)
+
+ # Load the input image FFT.
+ call rg_rload (Memr[ibuf], nrcols, nrlines, Memr[imfft], nxfft,
+ nyfft)
+
+ # Shift the data for easy of filtering.
+ call rg_fshift (Memr[imfft], Memr[imfft], 2 * nxfft, nyfft)
+
+ # Compute the Fourier Transform of the input image data.
+ Memi[dim] = nxfft
+ Memi[dim+1] = nyfft
+ if (Memi[dim+1] == 1)
+ call rg_fourn (Memr[imfft], Memi[dim], 1, 1)
+ else
+ call rg_fourn (Memr[imfft], Memi[dim], 2, 1)
+
+ # Compute the flux ratio between the two data sets.
+ if (IS_INDEFR(rg_pstatr(pm, UFLUXRATIO)))
+ call rg_psetr (pm, FLUXRATIO, rg_pg1norm (Memr[reffft],
+ 2 * nxfft, nyfft) / rg_pg1norm (Memr[imfft], 2 * nxfft,
+ nyfft))
+ else
+ call rg_psetr (pm, FLUXRATIO, rg_pstatr(pm, UFLUXRATIO))
+
+ # Divide the two functions.
+ call adivx (Memr[reffft], Memr[imfft], Memr[fft], nxfft * nyfft)
+
+ }
+
+ # Normalize the FFT.
+ call rg_pnorm (Memr[fft], nxfft, nyfft, rg_pstatr (pm, FLUXRATIO))
+
+ # Free the data buffers.
+ if (rbuf != NULL)
+ call mfree (rbuf, TY_REAL)
+ if (ibuf != NULL)
+ call mfree (ibuf, TY_REAL)
+
+ call sfree (sp)
+
+ return (OK)
+end
+
+
+# RG_KGET -- Read in the convolution kernel.
+
+int procedure rg_kget (pm, imk)
+
+pointer pm #I pointer to the psfmatch structure
+pointer imk #I pointer to the kernel image
+
+int nrlines
+pointer conv
+pointer rg_pstatp(), rg_pgdata()
+
+begin
+ if (IM_NDIM(imk) == 1)
+ nrlines = 1
+ else
+ nrlines = IM_LEN(imk,2)
+ conv = rg_pstatp (pm, CONV)
+ if (conv != NULL)
+ call mfree (conv, TY_REAL)
+ conv = rg_pgdata (imk, 1, int(IM_LEN(imk,1)), 1, nrlines)
+ call rg_psetp (pm, CONV, conv)
+
+ return (OK)
+end
+
+
+# RG_PFILTER -- Procedure to filter the FFT in frequency space.
+
+procedure rg_pfilter (pm)
+
+pointer pm #I pointer to the psf matching structure
+
+pointer sp, dim, psfft, conv
+real nfactor
+int rg_pstati()
+pointer rg_pstatp()
+real rg_pstatr(), asumr()
+
+begin
+ call smark (sp)
+ call salloc (dim, 2, TY_INT)
+
+ # Allocate space for the fourier spectrum.
+ if (rg_pstatp (pm, ASFFT) != NULL)
+ call mfree (rg_pstatp (pm, ASFFT), TY_REAL)
+ call calloc (psfft, rg_pstati (pm, NXFFT) * rg_pstati (pm, NYFFT),
+ TY_REAL)
+ call rg_psetp (pm, ASFFT, psfft)
+
+ # Allocate space for the convolution kernel.
+ if (rg_pstatp (pm, CONV) != NULL)
+ call mfree (rg_pstatp (pm, CONV), TY_REAL)
+ call malloc (conv, 2 * rg_pstati (pm, NXFFT) * rg_pstati (pm, NYFFT),
+ TY_REAL)
+ call rg_psetp (pm, CONV, conv)
+ call amovr (Memr[rg_pstatp(pm,FFT)], Memr[rg_pstatp(pm,CONV)],
+ 2 * rg_pstati (pm, NXFFT) * rg_pstati (pm, NYFFT))
+
+# # Compute the zextend parameter.
+# call rg_psetr (pm, THRESHOLD, rg_pstatr (pm, PRATIO) *
+# rg_gnorm (Memr[rg_pstatp(pm,IMFFT)], rg_pstati(pm,NXFFT),
+# rg_pstati(pm,NYFFT)))
+
+ # Filter the frequency spectrum.
+ switch (rg_pstati(pm,FILTER)) {
+ case PM_FCOSBELL:
+ call rg_pcosbell (Memr[rg_pstatp(pm,CONV)], rg_pstati (pm, NXFFT),
+ rg_pstati (pm, NYFFT), rg_pstatr (pm, SXINNER), rg_pstatr (pm,
+ SXOUTER), rg_pstatr (pm, SYINNER), rg_pstatr (pm, SYOUTER),
+ rg_pstati (pm, RADSYM))
+ case PM_FREPLACE:
+ call rg_preplace (Memr[rg_pstatp(pm,CONV)], Memr[rg_pstatp(pm,
+ IMFFT)], rg_pstati (pm, NXFFT), rg_pstati (pm, NYFFT),
+ rg_pstatr (pm,THRESHOLD), rg_pstatr (pm,FLUXRATIO))
+ case PM_FMODEL:
+ call rg_pgmodel (Memr[rg_pstatp(pm,CONV)], Memr[rg_pstatp(pm,
+ IMFFT)], rg_pstati (pm, NXFFT), rg_pstati (pm, NYFFT),
+ rg_pstatr (pm, THRESHOLD), rg_pstatr (pm, FLUXRATIO))
+ default:
+ ;
+ }
+
+ # Filter out any values greater than the normalization.
+ call rg_pnormfilt (Memr[rg_pstatp(pm,CONV)], rg_pstati(pm,NXFFT),
+ rg_pstati(pm,NYFFT), rg_pstatr (pm, FLUXRATIO))
+
+ # Compute the fourier spectrum.
+ call rg_pfourier (Memr[rg_pstatp(pm,CONV)], Memr[rg_pstatp(pm,ASFFT)],
+ rg_pstati(pm,NXFFT), rg_pstati(pm,NYFFT))
+
+ Memi[dim] = rg_pstati (pm, NXFFT)
+ Memi[dim+1] = rg_pstati (pm, NYFFT)
+ call rg_fshift (Memr[rg_pstatp(pm,CONV)], Memr[rg_pstatp(pm,CONV)],
+ 2 * rg_pstati(pm, NXFFT), rg_pstati(pm, NYFFT))
+ call rg_fourn (Memr[rg_pstatp(pm,CONV)], Memi[dim], 2, -1)
+ call rg_fshift (Memr[rg_pstatp(pm,CONV)], Memr[rg_pstatp(pm,CONV)],
+ 2 * rg_pstati(pm, NXFFT), rg_pstati(pm, NYFFT))
+ call adivkr (Memr[rg_pstatp(pm,CONV)], real (rg_pstati(pm,NXFFT) *
+ rg_pstati(pm,NYFFT)), Memr[rg_pstatp(pm,CONV)], 2 * rg_pstati(pm,
+ NXFFT) * rg_pstati(pm,NYFFT))
+
+ # Unpack the convolution kernel.
+ call rg_movexr (Memr[rg_pstatp(pm,CONV)], rg_pstati(pm,NXFFT),
+ rg_pstati(pm,NYFFT), Memr[rg_pstatp(pm,CONV)], rg_pstati(pm,KNX),
+ rg_pstati(pm,KNY))
+
+ # Normalize the kernel.
+ if (! IS_INDEFR(rg_pstatr (pm, NORMFACTOR))) {
+ nfactor = rg_pstatr (pm, NORMFACTOR) / asumr (Memr[rg_pstatp(pm,
+ CONV)], rg_pstati (pm, KNX) * rg_pstati(pm,KNY))
+ call amulkr (Memr[rg_pstatp (pm,CONV)], nfactor,
+ Memr[rg_pstatp(pm, CONV)], rg_pstati (pm, KNX) *
+ rg_pstati (pm, KNY))
+ }
+
+ # Reallocate the convolution kernel array
+ #conv = rg_pstatp (pm, CONV)
+ #if (conv != NULL) {
+ #call realloc (conv, rg_pstati(pm, KNX) * rg_pstati(pm, KNY),
+ #TY_REAL)
+ #call rg_psetp (pm, CONV, conv)
+ #}
+
+ call sfree (sp)
+end
+
+
+# RG_PGDATA -- Fill a buffer from a specified region of the image.
+
+pointer procedure rg_pgdata (im, c1, c2, l1, l2)
+
+pointer im #I pointer to the iraf image
+int c1, c2 #I column limits in the input image
+int l1, l2 #I line limits in the input image
+
+int i, ncols, nlines, npts
+pointer ptr, index, buf
+pointer imgs1r(), imgs2r()
+
+begin
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+ npts = ncols * nlines
+ call malloc (ptr, npts, TY_REAL)
+
+ index = ptr
+ do i = l1, l2 {
+ if (IM_NDIM(im) == 1)
+ buf = imgs1r (im, c1, c2)
+ else
+ buf = imgs2r (im, c1, c2, i, i)
+ call amovr (Memr[buf], Memr[index], ncols)
+ index = index + ncols
+ }
+
+ return (ptr)
+end
+
+
+# RG_PNSUM -- Compute the total intensity in the subtracted subraster.
+
+real procedure rg_pnsum (data, ncols, nlines, nxdata, nydata)
+
+real data[ncols,nlines] #I the input data subraster
+int ncols, nlines #I the size of the input subraster
+int nxdata, nydata #I the size of the data region
+
+int j, wxborder, wyborder, npts
+real sum
+bool fp_equalr()
+real asumr()
+
+begin
+ wxborder = (ncols - nxdata) / 2
+ wyborder = (nlines - nydata) / 2
+
+ sum = 0.0
+ npts = 0
+ do j = 1 + wyborder, nlines - wyborder {
+ sum = sum + asumr (data[1+wxborder,j], nxdata)
+ npts = npts + nxdata
+ }
+ if (npts <= 0 || fp_equalr (sum, 0.0))
+ return (INDEFR)
+ else
+ return (sum)
+end
+
+
+# RG_PWRITE -- Save the convolution kernel and the fourier spectrum of the
+# convolution kernel in an image.
+
+procedure rg_pwrite (pm, imk, imf)
+
+pointer pm #I pointer to psf matching structure
+pointer imk #I pointer to kernel image
+pointer imf #I pointer to fourier spectrum image
+
+int nx, ny
+pointer buf
+int rg_pstati()
+pointer rg_pstatp(), imps2r()
+
+begin
+ # Write out the kernel image.
+ if (imk != NULL && rg_pstatp(pm, CONV) != NULL) {
+ nx = rg_pstati (pm, KNX)
+ ny = rg_pstati (pm, KNY)
+ IM_NDIM(imk) = 2
+ IM_LEN(imk,1) = nx
+ IM_LEN(imk,2) = ny
+ IM_PIXTYPE(imk) = TY_REAL
+ buf = imps2r (imk, 1, nx, 1, ny)
+ if (rg_pstatp (pm, CONV) != NULL)
+ call amovr (Memr[rg_pstatp(pm,CONV)], Memr[buf], nx * ny)
+ else
+ call amovkr (0.0, Memr[buf], nx * ny)
+ }
+
+ # Write out the fourier spectrum.
+ if (imf != NULL && rg_pstatp(pm,ASFFT) != NULL) {
+ nx = rg_pstati (pm, NXFFT)
+ ny = rg_pstati (pm, NYFFT)
+ IM_NDIM(imf) = 2
+ IM_LEN(imf,1) = nx
+ IM_LEN(imf,2) = ny
+ IM_PIXTYPE(imf) = TY_REAL
+ buf = imps2r (imf, 1, nx, 1, ny)
+ if (rg_pstatp (pm, CONV) != NULL)
+ call amovr (Memr[rg_pstatp(pm,ASFFT)], Memr[buf], nx * ny)
+ else
+ call amovkr (0.0, Memr[buf], nx * ny)
+ }
+end
+
diff --git a/pkg/images/immatch/src/psfmatch/rgpshow.x b/pkg/images/immatch/src/psfmatch/rgpshow.x
new file mode 100644
index 00000000..c94349a6
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgpshow.x
@@ -0,0 +1,116 @@
+include "psfmatch.h"
+
+# RG_PSHOW -- Print the PSFMATCH task parameters.
+
+procedure rg_pshow (pm)
+
+pointer pm #I pointer to psfmatch structure
+
+pointer sp, str
+bool itob()
+int rg_pstati()
+real rg_pstatr()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ call rg_pstats (pm, CSTRING, Memc[str], SZ_FNAME)
+ call printf ("\nConvolution: %s\n")
+ call pargstr (Memc[str])
+ if (rg_pstati (pm, CONVOLUTION) == PM_CONIMAGE) {
+ call rg_pstats (pm, IMAGE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ call rg_pstats (pm, REFIMAGE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str])
+ call rg_pstats (pm, PSFDATA, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_PSFDATA)
+ call pargstr (Memc[str])
+ } else if (rg_pstati (pm, CONVOLUTION) == PM_CONPSF) {
+ call rg_pstats (pm, IMAGE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ call rg_pstats (pm, PSFIMAGE, Memc[str], SZ_FNAME)
+ call printf (" input psf: %s\n")
+ call pargstr (Memc[str])
+ call rg_pstats (pm, REFIMAGE, Memc[str], SZ_FNAME)
+ call printf (" reference psf: %s\n")
+ call pargstr (Memc[str])
+ } else {
+ call rg_pstats (pm, IMAGE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ }
+
+ call rg_pstats (pm, KERNEL, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_KERNEL)
+ call pargstr (Memc[str])
+ call rg_pstats (pm, OUTIMAGE, Memc[str], SZ_FNAME)
+ if (Memc[str] != EOS) {
+ call printf (" %s: %s\n")
+ call pargstr (KY_OUTIMAGE)
+ call pargstr (Memc[str])
+ }
+
+ call printf ("Centering and background fitting\n")
+ call printf (" %s: %b\n")
+ call pargstr (KY_CENTER)
+ call pargb (itob(rg_pstati(pm,CENTER)))
+ call rg_pstats (pm, BSTRING, Memc[str], SZ_LINE)
+ call printf (" %s: %s\n")
+ call pargstr (KY_BACKGRD)
+ call pargstr (Memc[str])
+ call printf (" %s = %g %s = %g\n")
+ call pargstr (KY_LOREJECT)
+ call pargr (rg_pstatr (pm, LOREJECT))
+ call pargstr (KY_HIREJECT)
+ call pargr (rg_pstatr (pm, HIREJECT))
+ call printf (" %s = %g\n")
+ call pargstr (KY_APODIZE)
+ call pargr (rg_pstatr (pm, APODIZE))
+
+ call printf ("Filtering:\n")
+ call rg_pstats (pm, FSTRING, Memc[str], SZ_LINE)
+ call printf (" %s: %s\n")
+ call pargstr (KY_FILTER)
+ call pargstr (Memc[str])
+ if (rg_pstati(pm,FILTER) == PM_FCOSBELL) {
+ call printf (" %s: %g %s: %g\n")
+ call pargstr (KY_SXINNER)
+ call pargr (rg_pstatr (pm, SXINNER))
+ call pargstr (KY_SXOUTER)
+ call pargr (rg_pstatr (pm, SXOUTER))
+ call printf (" %s: %g %s: %g\n")
+ call pargstr (KY_SYINNER)
+ call pargr (rg_pstatr (pm, SYINNER))
+ call pargstr (KY_SYOUTER)
+ call pargr (rg_pstatr (pm, SYOUTER))
+ call printf (" %s: %b\n")
+ call pargstr (KY_RADSYM)
+ call pargb (itob(rg_pstati(pm,RADSYM)))
+ } else {
+ call printf (" %s: %g\n")
+ call pargstr (KY_UFLUXRATIO)
+ call pargr (rg_pstatr (pm, UFLUXRATIO))
+ call printf (" %s: %g\n")
+ call pargstr (KY_THRESHOLD)
+ call pargr (rg_pstatr(pm,THRESHOLD))
+ }
+
+ call printf ("Normalization\n")
+ call printf (" %s: %g\n")
+ call pargstr (KY_NORMFACTOR)
+ call pargr (rg_pstatr (pm, NORMFACTOR))
+
+ call printf ("\n")
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/psfmatch/rgptools.x b/pkg/images/immatch/src/psfmatch/rgptools.x
new file mode 100644
index 00000000..df36c166
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/rgptools.x
@@ -0,0 +1,641 @@
+include "psfmatch.h"
+
+# RG_PINIT -- Initialize the main psfmatch data structure.
+
+procedure rg_pinit (pm, cfunc)
+
+pointer pm #O pointer to psfmatch structure
+int cfunc #I mode of computing the convolution function
+
+begin
+ call malloc (pm, LEN_PSFSTRUCT, TY_STRUCT)
+
+ # Initialize the pointers.
+ PM_RC1(pm) = NULL
+ PM_RC2(pm) = NULL
+ PM_RL1(pm) = NULL
+ PM_RL2(pm) = NULL
+ PM_RZERO(pm) = NULL
+ PM_RXSLOPE(pm) = NULL
+ PM_RYSLOPE(pm) = NULL
+ PM_NREGIONS(pm) = 0
+ PM_CNREGION(pm) = 1
+
+ # Define the background fitting parameters.
+ PM_CENTER(pm) = DEF_CENTER
+ PM_BACKGRD(pm) = DEF_BACKGRD
+ PM_BVALUER(pm) = 0.0
+ PM_BVALUE(pm) = 0.0
+ call strcpy ("median", PM_BSTRING(pm), SZ_FNAME)
+ PM_LOREJECT(pm) = DEF_LOREJECT
+ PM_HIREJECT(pm) = DEF_HIREJECT
+ PM_APODIZE(pm) = 0.0
+
+ PM_UFLUXRATIO(pm) = DEF_UFLUXRATIO
+ PM_FILTER(pm) = DEF_FILTER
+ call strcpy ("replace", PM_FSTRING(pm), SZ_FNAME)
+ PM_SXINNER(pm) = DEF_SXINNER
+ PM_SXOUTER(pm) = DEF_SXOUTER
+ PM_SYINNER(pm) = DEF_SYINNER
+ PM_SYOUTER(pm) = DEF_SYOUTER
+ PM_RADSYM(pm) = DEF_RADSYM
+ PM_THRESHOLD(pm) = DEF_THRESHOLD
+
+ PM_NORMFACTOR(pm) = DEF_NORMFACTOR
+
+ PM_CONVOLUTION(pm) = cfunc
+ switch (cfunc) {
+ case PM_CONIMAGE:
+ PM_CONVOLUTION(pm) = PM_CONIMAGE
+ call strcpy ("image", PM_CSTRING(pm), SZ_FNAME)
+ case PM_CONPSF:
+ PM_CONVOLUTION(pm) = PM_CONPSF
+ call strcpy ("psf", PM_CSTRING(pm), SZ_FNAME)
+ case PM_CONKERNEL:
+ PM_CONVOLUTION(pm) = PM_CONKERNEL
+ call strcpy ("kernel", PM_CSTRING(pm), SZ_FNAME)
+ default:
+ PM_CONVOLUTION(pm) = PM_CONIMAGE
+ call strcpy ("image", PM_CSTRING(pm), SZ_FNAME)
+ }
+ PM_DNX(pm) = DEF_DNX
+ PM_DNY(pm) = DEF_DNY
+ PM_PNX(pm) = DEF_PNX
+ PM_PNY(pm) = DEF_PNY
+ PM_KNX(pm) = 0
+ PM_KNY(pm) = 0
+ PM_POWER(pm) = DEF_POWER
+
+ PM_REFFFT(pm) = NULL
+ PM_IMFFT(pm) = NULL
+ PM_FFT(pm) = NULL
+ PM_CONV(pm) = NULL
+ PM_ASFFT(pm) = NULL
+ PM_NXFFT(pm) = 0
+ PM_NYFFT(pm) = 0
+
+ # Initialize the strings.
+ PM_IMAGE(pm) = EOS
+ PM_REFIMAGE(pm) = EOS
+ PM_PSFDATA(pm) = EOS
+ PM_PSFIMAGE(pm) = EOS
+ PM_OBJLIST(pm) = EOS
+ PM_KERNEL(pm) = EOS
+ PM_OUTIMAGE(pm) = EOS
+
+ # Initialize the buffers.
+ call rg_prinit (pm)
+end
+
+
+# RG_PRINIT -- Initialize the regions definition portion of the psf matching
+# code fitting structure.
+
+procedure rg_prinit (pm)
+
+pointer pm #I pointer to psfmatch structure
+
+begin
+ call rg_prfree (pm)
+
+ PM_NREGIONS(pm) = 0
+ PM_CNREGION(pm) = 1
+
+ call malloc (PM_RC1(pm), MAX_NREGIONS, TY_INT)
+ call malloc (PM_RC2(pm), MAX_NREGIONS, TY_INT)
+ call malloc (PM_RL1(pm), MAX_NREGIONS, TY_INT)
+ call malloc (PM_RL2(pm), MAX_NREGIONS, TY_INT)
+ call malloc (PM_RZERO(pm), MAX_NREGIONS, TY_REAL)
+ call malloc (PM_RXSLOPE(pm), MAX_NREGIONS, TY_REAL)
+ call malloc (PM_RYSLOPE(pm), MAX_NREGIONS, TY_REAL)
+
+ call amovki (INDEFI, Memi[PM_RC1(pm)], MAX_NREGIONS)
+ call amovki (INDEFI, Memi[PM_RC2(pm)], MAX_NREGIONS)
+ call amovki (INDEFI, Memi[PM_RL1(pm)], MAX_NREGIONS)
+ call amovki (INDEFI, Memi[PM_RL2(pm)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[PM_RZERO(pm)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[PM_RXSLOPE(pm)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[PM_RYSLOPE(pm)], MAX_NREGIONS)
+end
+
+
+# RG_PINDEFR -- Re-initialize the background and answers regions portion of
+# the psf-matching structure.
+
+procedure rg_pindefr (pm)
+
+pointer pm #I pointer to the psfmatch structure
+
+int nregions
+int rg_pstati ()
+
+begin
+ nregions = rg_pstati (pm, NREGIONS)
+
+ if (nregions > 0) {
+ call amovkr (INDEFR, Memr[PM_RZERO(pm)], nregions)
+ call amovkr (INDEFR, Memr[PM_RXSLOPE(pm)], nregions)
+ call amovkr (INDEFR, Memr[PM_RYSLOPE(pm)], nregions)
+ }
+end
+
+
+# RG_PREALLOC -- Reallocate the regions buffers and initialize if necessary.
+
+procedure rg_prealloc (pm, nregions)
+
+pointer pm #I pointer to psfmatch structure
+int nregions #I number of regions
+
+int nr
+int rg_pstati()
+
+begin
+ nr = rg_pstati (pm, NREGIONS)
+
+ call realloc (PM_RC1(pm), nregions, TY_INT)
+ call realloc (PM_RC2(pm), nregions, TY_INT)
+ call realloc (PM_RL1(pm), nregions, TY_INT)
+ call realloc (PM_RL2(pm), nregions, TY_INT)
+ call realloc (PM_RZERO(pm), nregions, TY_REAL)
+ call realloc (PM_RXSLOPE(pm), nregions, TY_REAL)
+ call realloc (PM_RYSLOPE(pm), nregions, TY_REAL)
+
+ call amovki (INDEFI, Memi[PM_RC1(pm)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[PM_RC2(pm)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[PM_RL1(pm)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[PM_RL2(pm)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[PM_RZERO(pm)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[PM_RXSLOPE(pm)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[PM_RYSLOPE(pm)+nr], nregions - nr)
+ #call amovkr (INDEFR, Memr[PM_XSHIFTS(pm)+nr], nregions - nr)
+ #call amovkr (INDEFR, Memr[PM_YSHIFTS(pm)+nr], nregions - nr)
+end
+
+
+# RG_PRFREE -- Free the regions portion of the psfmatch structure.
+
+procedure rg_prfree (pm)
+
+pointer pm #I/O pointer to psfmatch structure
+
+begin
+ call rg_pseti (pm, NREGIONS, 0)
+ if (PM_RC1(pm) != NULL)
+ call mfree (PM_RC1(pm), TY_INT)
+ PM_RC1(pm) = NULL
+ if (PM_RC2(pm) != NULL)
+ call mfree (PM_RC2(pm), TY_INT)
+ PM_RC2(pm) = NULL
+ if (PM_RL1(pm) != NULL)
+ call mfree (PM_RL1(pm), TY_INT)
+ PM_RL1(pm) = NULL
+ if (PM_RL2(pm) != NULL)
+ call mfree (PM_RL2(pm), TY_INT)
+ PM_RL2(pm) = NULL
+ if (PM_RZERO(pm) != NULL)
+ call mfree (PM_RZERO(pm), TY_REAL)
+ PM_RZERO(pm) = NULL
+ if (PM_RXSLOPE(pm) != NULL)
+ call mfree (PM_RXSLOPE(pm), TY_REAL)
+ PM_RXSLOPE(pm) = NULL
+ if (PM_RYSLOPE(pm) != NULL)
+ call mfree (PM_RYSLOPE(pm), TY_REAL)
+ PM_RYSLOPE(pm) = NULL
+end
+
+
+# RG_PFREE -- Free the psfmatch structure.
+
+procedure rg_pfree (pm)
+
+pointer pm #I pointer to psfmatch structure
+
+begin
+ # Free the region descriptors
+ call rg_prfree (pm)
+
+ if (PM_REFFFT(pm) != NULL)
+ call mfree (PM_REFFFT(pm), TY_REAL)
+ if (PM_IMFFT(pm) != NULL)
+ call mfree (PM_IMFFT(pm), TY_REAL)
+ if (PM_FFT(pm) != NULL)
+ call mfree (PM_FFT(pm), TY_REAL)
+ if (PM_CONV(pm) != NULL)
+ call mfree (PM_CONV(pm), TY_REAL)
+ if (PM_ASFFT(pm) != NULL)
+ call mfree (PM_ASFFT(pm), TY_REAL)
+
+ call mfree (pm, TY_STRUCT)
+end
+
+
+# RG_PSTATI -- Fetch the value of a psfmatch task integer parameter.
+
+int procedure rg_pstati (pm, param)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+
+begin
+ switch (param) {
+ case NREGIONS:
+ return (PM_NREGIONS(pm))
+ case CNREGION:
+ return (PM_CNREGION(pm))
+ case CENTER:
+ return (PM_CENTER(pm))
+ case BACKGRD:
+ return (PM_BACKGRD(pm))
+ case CONVOLUTION:
+ return (PM_CONVOLUTION(pm))
+ case DNX:
+ return (PM_DNX(pm))
+ case DNY:
+ return (PM_DNY(pm))
+ case PNX:
+ return (PM_PNX(pm))
+ case PNY:
+ return (PM_PNY(pm))
+ case KNX:
+ return (PM_KNX(pm))
+ case KNY:
+ return (PM_KNY(pm))
+ case POWER:
+ return (PM_POWER(pm))
+
+ case FILTER:
+ return (PM_FILTER(pm))
+ case RADSYM:
+ return (PM_RADSYM(pm))
+
+ case NXFFT:
+ return (PM_NXFFT(pm))
+ case NYFFT:
+ return (PM_NYFFT(pm))
+
+ default:
+ call error (0, "RG_PSTATI: Unknown integer parameter.")
+ }
+end
+
+
+# RG_PSTATP -- Fetch the value of a psfmatch task pointer parameter.
+
+pointer procedure rg_pstatp (pm, param)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+
+begin
+ switch (param) {
+ case RC1:
+ return (PM_RC1(pm))
+ case RC2:
+ return (PM_RC2(pm))
+ case RL1:
+ return (PM_RL1(pm))
+ case RL2:
+ return (PM_RL2(pm))
+ case RZERO:
+ return (PM_RZERO(pm))
+ case RXSLOPE:
+ return (PM_RXSLOPE(pm))
+ case RYSLOPE:
+ return (PM_RYSLOPE(pm))
+ case REFFFT:
+ return (PM_REFFFT(pm))
+ case IMFFT:
+ return (PM_IMFFT(pm))
+ case FFT:
+ return (PM_FFT(pm))
+ case CONV:
+ return (PM_CONV(pm))
+ case ASFFT:
+ return (PM_ASFFT(pm))
+ default:
+ call error (0, "RG_PSTATP: Unknown pointer parameter.")
+ }
+end
+
+
+# RG_PSTATR -- Fetch the value of a psfmath task real parameter.
+
+real procedure rg_pstatr (pm, param)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+
+begin
+ switch (param) {
+ case BVALUER:
+ return (PM_BVALUER(pm))
+ case BVALUE:
+ return (PM_BVALUE(pm))
+ case APODIZE:
+ return (PM_APODIZE(pm))
+ case LOREJECT:
+ return (PM_LOREJECT(pm))
+ case HIREJECT:
+ return (PM_HIREJECT(pm))
+ case UFLUXRATIO:
+ return (PM_UFLUXRATIO(pm))
+ case FLUXRATIO:
+ return (PM_FLUXRATIO(pm))
+ case SXINNER:
+ return (PM_SXINNER(pm))
+ case SXOUTER:
+ return (PM_SXOUTER(pm))
+ case SYINNER:
+ return (PM_SYINNER(pm))
+ case SYOUTER:
+ return (PM_SYOUTER(pm))
+ case THRESHOLD:
+ return (PM_THRESHOLD(pm))
+ case NORMFACTOR:
+ return (PM_NORMFACTOR(pm))
+ default:
+ call error (0, "RG_PSTATR: Unknown real parameter.")
+ }
+end
+
+
+# RG_PSTATS -- Fetch the value of a psfmatch string string parameter.
+
+procedure rg_pstats (pm, param, str, maxch)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+char str[ARB] # output string
+int maxch # maximum number of characters
+
+begin
+ switch (param) {
+ case BSTRING:
+ call strcpy (PM_BSTRING(pm), str, maxch)
+ case CSTRING:
+ call strcpy (PM_CSTRING(pm), str, maxch)
+ case FSTRING:
+ call strcpy (PM_FSTRING(pm), str, maxch)
+ case IMAGE:
+ call strcpy (PM_IMAGE(pm), str, maxch)
+ case REFIMAGE:
+ call strcpy (PM_REFIMAGE(pm), str, maxch)
+ case PSFDATA:
+ call strcpy (PM_PSFDATA(pm), str, maxch)
+ case PSFIMAGE:
+ call strcpy (PM_PSFIMAGE(pm), str, maxch)
+ case OBJLIST:
+ call strcpy (PM_OBJLIST(pm), str, maxch)
+ case KERNEL:
+ call strcpy (PM_KERNEL(pm), str, maxch)
+ case OUTIMAGE:
+ call strcpy (PM_OUTIMAGE(pm), str, maxch)
+ default:
+ call error (0, "RG_PSTATS: Unknown string parameter.")
+ }
+end
+
+
+# RG_PSETI -- Set the value of a psfmatch task integer parameter.
+
+procedure rg_pseti (pm, param, value)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+int value # value of the integer parameter
+
+begin
+ switch (param) {
+ case NREGIONS:
+ PM_NREGIONS(pm) = value
+ case CNREGION:
+ PM_CNREGION(pm) = value
+ case CENTER:
+ PM_CENTER(pm) = value
+ case BACKGRD:
+ PM_BACKGRD(pm) = value
+ switch (value) {
+ case PM_BNONE:
+ call strcpy ("none", PM_BSTRING(pm), SZ_FNAME)
+ case PM_BMEAN:
+ call strcpy ("mean", PM_BSTRING(pm), SZ_FNAME)
+ case PM_BMEDIAN:
+ call strcpy ("median", PM_BSTRING(pm), SZ_FNAME)
+ case PM_BSLOPE:
+ call strcpy ("plane", PM_BSTRING(pm), SZ_FNAME)
+ case PM_BNUMBER:
+ ;
+ default:
+ call strcpy ("none", PM_BSTRING(pm), SZ_FNAME)
+ }
+ case CONVOLUTION:
+ PM_CONVOLUTION(pm) = value
+ switch (value) {
+ case PM_CONIMAGE:
+ call strcpy ("image", PM_CSTRING(pm), SZ_FNAME)
+ case PM_CONPSF:
+ call strcpy ("psf", PM_CSTRING(pm), SZ_FNAME)
+ case PM_CONKERNEL:
+ call strcpy ("kernel", PM_CSTRING(pm), SZ_FNAME)
+ default:
+ call strcpy ("image", PM_CSTRING(pm), SZ_FNAME)
+ }
+ case DNX:
+ PM_DNX(pm) = value
+ case DNY:
+ PM_DNY(pm) = value
+ case PNX:
+ PM_PNX(pm) = value
+ case PNY:
+ PM_PNY(pm) = value
+ case KNX:
+ PM_KNX(pm) = value
+ case KNY:
+ PM_KNY(pm) = value
+ case POWER:
+ PM_POWER(pm) = value
+ case RADSYM:
+ PM_RADSYM(pm) = value
+ case NXFFT:
+ PM_NXFFT(pm) = value
+ case NYFFT:
+ PM_NYFFT(pm) = value
+ case FILTER:
+ PM_FILTER(pm) = value
+ switch (value) {
+ case PM_FNONE:
+ call strcpy ("none", PM_FSTRING(pm), SZ_FNAME)
+ case PM_FCOSBELL:
+ call strcpy ("cosbell", PM_FSTRING(pm), SZ_FNAME)
+ case PM_FREPLACE:
+ call strcpy ("replace", PM_FSTRING(pm), SZ_FNAME)
+ case PM_FMODEL:
+ call strcpy ("model", PM_FSTRING(pm), SZ_FNAME)
+ default:
+ call strcpy ("none", PM_FSTRING(pm), SZ_FNAME)
+ }
+ default:
+ call error (0, "RG_PSETI: Unknown integer parameter.")
+ }
+end
+
+
+# RG_PSETP -- Set the value of a psfmatch task pointer parameter.
+
+procedure rg_psetp (pm, param, value)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+pointer value # value of the pointer parameter
+
+begin
+ switch (param) {
+ case RC1:
+ PM_RC1(pm) = value
+ case RC2:
+ PM_RC2(pm) = value
+ case RL1:
+ PM_RL1(pm) = value
+ case RL2:
+ PM_RL2(pm) = value
+ case RZERO:
+ PM_RZERO(pm) = value
+ case RXSLOPE:
+ PM_RXSLOPE(pm) = value
+ case RYSLOPE:
+ PM_RYSLOPE(pm) = value
+ case REFFFT:
+ PM_REFFFT(pm) = value
+ case IMFFT:
+ PM_IMFFT(pm) = value
+ case FFT:
+ PM_FFT(pm) = value
+ case CONV:
+ PM_CONV(pm) = value
+ case ASFFT:
+ PM_ASFFT(pm) = value
+
+ default:
+ call error (0, "RG_PSETP: Unknown pointer parameter.")
+ }
+end
+
+
+# RG_PSETR -- Set the value of a psfmatch task real parameter.
+
+procedure rg_psetr (pm, param, value)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+real value # real parameter
+
+begin
+ switch (param) {
+ case BVALUER:
+ PM_BVALUER(pm) = value
+ case BVALUE:
+ PM_BVALUE(pm) = value
+ case LOREJECT:
+ PM_LOREJECT(pm) = value
+ case HIREJECT:
+ PM_HIREJECT(pm) = value
+ case APODIZE:
+ PM_APODIZE(pm) = value
+ case UFLUXRATIO:
+ PM_UFLUXRATIO(pm) = value
+ case FLUXRATIO:
+ PM_FLUXRATIO(pm) = value
+ case SXINNER:
+ PM_SXINNER(pm) = value
+ case SXOUTER:
+ PM_SXOUTER(pm) = value
+ case SYINNER:
+ PM_SYINNER(pm) = value
+ case SYOUTER:
+ PM_SYOUTER(pm) = value
+ case THRESHOLD:
+ PM_THRESHOLD(pm) = value
+ case NORMFACTOR:
+ PM_NORMFACTOR(pm) = value
+ default:
+ call error (0, "RG_PSETR: Unknown real parameter.")
+ }
+end
+
+
+# RG_PSETS -- Procedure to set the value of a string parameter.
+
+procedure rg_psets (pm, param, str)
+
+pointer pm # pointer to psfmatch structure
+int param # parameter to be fetched
+char str[ARB] # output string
+
+int index, ip
+pointer sp, temp
+real rval
+int strdic(), fnldir(), ctor()
+
+begin
+ call smark (sp)
+ call salloc (temp, SZ_LINE, TY_CHAR)
+
+ switch (param) {
+ case BSTRING:
+ ip = 1
+ index = strdic (str, str, SZ_LINE, PM_BTYPES)
+ if (index > 0) {
+ call strcpy (str, PM_BSTRING(pm), SZ_FNAME)
+ call rg_pseti (pm, BACKGRD, index)
+ } else if (ctor (str, ip, rval) > 0) {
+ call rg_psetr (pm, BVALUE, rval)
+ if (ctor (str, ip, rval) > 0) {
+ call rg_psetr (pm, BVALUER, rval)
+ call strcpy (str, PM_BSTRING(pm), SZ_FNAME)
+ call rg_pseti (pm, BACKGRD, PM_NUMBER)
+ } else {
+ call rg_psetr (pm, BVALUE, 0.0)
+ call rg_psetr (pm, BVALUER, 0.0)
+ }
+ }
+ case CSTRING:
+ index = strdic (str, str, SZ_LINE, PM_CTYPES)
+ if (index > 0) {
+ call strcpy (str, PM_CSTRING(pm), SZ_FNAME)
+ call rg_pseti (pm, CONVOLUTION, index)
+ }
+ case FSTRING:
+ index = strdic (str, str, SZ_LINE, PM_FTYPES)
+ if (index > 0) {
+ call strcpy (str, PM_FSTRING(pm), SZ_FNAME)
+ call rg_pseti (pm, FILTER, index)
+ }
+ case IMAGE:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], PM_IMAGE(pm), SZ_FNAME)
+ call strcpy (Memc[temp+index], PM_IMAGE(pm), SZ_FNAME)
+ case REFIMAGE:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], PM_REFIMAGE(pm), SZ_FNAME)
+ call strcpy (Memc[temp+index], PM_REFIMAGE(pm), SZ_FNAME)
+ case PSFDATA:
+ call strcpy (str, PM_PSFDATA(pm), SZ_FNAME)
+ case PSFIMAGE:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], PM_PSFIMAGE(pm), SZ_FNAME)
+ call strcpy (Memc[temp+index], PM_PSFIMAGE(pm), SZ_FNAME)
+ case OBJLIST:
+ call strcpy (str, PM_OBJLIST(pm), SZ_FNAME)
+ case KERNEL:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], PM_KERNEL(pm), SZ_FNAME)
+ call strcpy (Memc[temp+index], PM_KERNEL(pm), SZ_FNAME)
+ case OUTIMAGE:
+ call strcpy (str, PM_OUTIMAGE(pm), SZ_FNAME)
+ default:
+ call error (0, "RG_PSETS: Unknown string parameter.")
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/psfmatch/t_psfmatch.x b/pkg/images/immatch/src/psfmatch/t_psfmatch.x
new file mode 100644
index 00000000..182ac286
--- /dev/null
+++ b/pkg/images/immatch/src/psfmatch/t_psfmatch.x
@@ -0,0 +1,365 @@
+include <fset.h>
+include <imhdr.h>
+include "psfmatch.h"
+
+# T_PSFMATCH -- Match the resolution of an image to that of a reference
+# image.
+
+procedure t_psfmatch ()
+
+pointer image1 # pointer to the input image name
+pointer imager # pointer to the reference image name
+pointer fpsflist # pointer to the regions list
+pointer image2 # pointer to the output image name
+pointer kernel # pointer to the kernel image name
+pointer pspectra # pointer to the fourier spectra image name
+int interactive # interactive mode ?
+int verbose # verbose mode ?
+int boundary # boundary extension type
+real constant # constant for boundary extension
+
+int list1, listr, psflist, listk, list2
+int nregions, newref, stat
+pointer sp, imtemp, str, pm, gd, id, imr, im1, impsf, imk, im2
+bool clgetb()
+int imtopen(), imtlen(), imtgetim(), fntopnb(), fntlenb(), clgwrd(), btoi()
+int rg_pstati(), rg_ptmpimage(), rg_pregions(), rg_psfm(), rg_pisfm()
+pointer gopen(), immap(), rg_pstatp()
+real clgetr()
+errchk fntopnb(), fntclsb()
+
+begin
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call salloc (imager, SZ_FNAME, TY_CHAR)
+ call salloc (fpsflist, SZ_LINE, TY_CHAR)
+ call salloc (kernel, SZ_FNAME, TY_CHAR)
+ call salloc (image2, SZ_FNAME, TY_CHAR)
+ call salloc (pspectra, SZ_FNAME, TY_CHAR)
+ call salloc (imtemp, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get task parameters.
+ call clgstr ("input", Memc[str], SZ_LINE)
+ list1 = imtopen (Memc[str])
+ call clgstr ("reference", Memc[str], SZ_LINE)
+ listr = imtopen (Memc[str])
+ call clgstr ("psfdata", Memc[fpsflist], SZ_LINE)
+ call clgstr ("kernel", Memc[str], SZ_LINE)
+ listk = imtopen (Memc[str])
+ call clgstr ("output", Memc[str], SZ_LINE)
+ list2 = imtopen (Memc[str])
+
+ # Open the psf matching fitting structure.
+ call rg_pgpars (pm)
+
+ # Will the task run in interactive mode?
+ if (rg_pstati (pm, CONVOLUTION) == PM_CONKERNEL)
+ interactive = NO
+ else
+ interactive = btoi (clgetb ("interactive"))
+
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL) {
+ if (imtlen (listr) <= 0)
+ call error (0, "The reference image list is empty.")
+ if (imtlen (listr) > 1 && imtlen (listr) != imtlen (list1))
+ call error (0,
+ "The number of reference and input images is not the same.")
+ if (interactive == NO && Memc[fpsflist] == EOS) {
+ call error (0, "The objects list is empty.")
+ } else if (rg_pstati (pm, CONVOLUTION) == PM_CONIMAGE) {
+ psflist = fntopnb (Memc[fpsflist], NO)
+ if (fntlenb(psflist) > 0 && imtlen (listr) != fntlenb (psflist))
+ call error (0,
+ "The number of reference images and objects lists is not the same")
+ } else {
+ psflist = imtopen (Memc[fpsflist])
+ if (imtlen (list1) != imtlen (psflist))
+ call error (0,
+ "The number of input and psf images is not the same")
+ }
+ call rg_psets (pm, PSFDATA, Memc[fpsflist])
+ } else {
+ call imtclose (listr)
+ listr = NULL
+ psflist = NULL
+ call rg_psets (pm, PSFDATA, "")
+ }
+
+ # Compare the lengths of the input and output lists.
+ if (imtlen(listk) <= 0) {
+ call imtclose (listk)
+ listk = NULL
+ } else if (imtlen (list1) != imtlen (listk))
+ call error (0,
+ "The number of input and kernel images is not the same.")
+
+ if (imtlen (list2) <= 0) {
+ call imtclose (list2)
+ list2 = NULL
+ } else if (imtlen (list1) != imtlen (list2))
+ call error (0,
+ "The number of input and output images are not the same.")
+
+ # Get the boundary extension parameters for the image convolution.
+ boundary = clgwrd ("boundary", Memc[str], SZ_LINE,
+ "|constant|nearest|reflect|wrap|")
+ constant = clgetr ("constant")
+
+ if (interactive == YES) {
+ call clgstr ("graphics", Memc[str], SZ_FNAME)
+ iferr (gd = gopen (Memc[str], NEW_FILE, STDGRAPH))
+ gd = NULL
+ call clgstr ("display", Memc[str], SZ_FNAME)
+ iferr (id = gopen (Memc[str], APPEND, STDIMAGE))
+ id = NULL
+ verbose = YES
+ } else {
+ gd = NULL
+ id = NULL
+ verbose = btoi (clgetb ("verbose"))
+ }
+
+ imr = NULL
+ impsf = NULL
+
+ # Do each set of input and output images.
+ while ((imtgetim (list1, Memc[image1], SZ_FNAME) != EOF)) {
+
+ # Open reference image and the associated objects file
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL) {
+ if (imtgetim (listr, Memc[imager], SZ_FNAME) != EOF) {
+ if (imr != NULL)
+ call imunmap (imr)
+ imr = immap (Memc[imager], READ_ONLY, 0)
+ if (IM_NDIM(imr) > 2)
+ call error (0, "Reference psf/image must be 1D or 2D")
+ call rg_psets (pm, REFIMAGE, Memc[imager])
+ if (rg_pstati (pm, CONVOLUTION) == PM_CONIMAGE) {
+ nregions = rg_pregions (psflist, imr, pm, 1, NO)
+ if (nregions <= 0 && interactive == NO)
+ call error (0, "The objects list is empty.")
+ call rg_psets (pm, PSFIMAGE, "")
+ }
+ newref = YES
+ }
+ if (rg_pstati (pm, CONVOLUTION) == PM_CONPSF) {
+ if (imtgetim (psflist, Memc[str], SZ_FNAME) != EOF) {
+ impsf = immap (Memc[str], READ_ONLY, 0)
+ if (IM_NDIM(impsf) != IM_NDIM(imr))
+ call error (0,
+ "Image and reference psf must have same dimensionality")
+ if (IM_LEN(impsf,1) != IM_LEN(imr,1))
+ call error (0,
+ "Image and reference psf are not the same size")
+ if (IM_NDIM(impsf) == 2 && (IM_LEN(impsf,2) !=
+ IM_LEN(imr,2)))
+ call error (0,
+ "Image and reference psf are not the same size")
+ call rg_psets (pm, PSFIMAGE, Memc[str])
+ newref = YES
+ }
+ }
+ } else {
+ imr = NULL
+ impsf = NULL
+ call rg_psets (pm, REFIMAGE, "")
+ call rg_psets (pm, PSFIMAGE, "")
+ call rg_psets (pm, OBJLIST, "")
+ newref = NO
+ }
+
+ # Open the input image.
+ im1 = immap (Memc[image1], READ_ONLY, 0)
+ if (IM_NDIM(im1) > 2) {
+ call error (0, "Input image must be 1D or 2D")
+ } else if (imr != NULL) {
+ if (IM_NDIM(im1) != IM_NDIM(imr))
+ call error (0,
+ "Input and reference images must have same dimensionality")
+ }
+ call rg_psets (pm, IMAGE, Memc[image1])
+
+ # Open the kernel image name.
+ if (listk != NULL) {
+ if (imtgetim (listk, Memc[kernel], SZ_FNAME) != EOF)
+ ;
+ } else {
+ if (rg_ptmpimage (Memc[image1], "ker", "ker", Memc[kernel],
+ SZ_FNAME) == NO)
+ ;
+ }
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL)
+ imk = immap (Memc[kernel], NEW_IMAGE, 0)
+ else
+ imk = immap (Memc[kernel], READ_ONLY, 0)
+ call rg_psets (pm, KERNEL, Memc[kernel])
+
+
+ # Construct the output image name.
+ if (list2 == NULL) {
+ im2 = NULL
+ Memc[image2] = NULL
+ } else if (imtgetim (list2, Memc[image2], SZ_FNAME) != EOF) {
+ call xt_mkimtemp (Memc[image1], Memc[image2], Memc[imtemp],
+ SZ_FNAME)
+ im2 = immap (Memc[image2], NEW_COPY, im1)
+ } else {
+ im2 = NULL
+ Memc[image2] = NULL
+ }
+ call rg_psets (pm, OUTIMAGE, Memc[image2])
+
+ # Compute the the psf matching kernel.
+ if (interactive == YES) {
+ stat = rg_pisfm (pm, imr, psflist, impsf, im1, imk, NULL, im2,
+ gd, id)
+ } else {
+ if (rg_psfm (pm, imr, im1, impsf, imk, newref) == OK) {
+ if (verbose == YES) {
+ call printf (
+ "Completed computing/reading kernel %s for image %s\n")
+ call pargstr (Memc[kernel])
+ call pargstr (Memc[image1])
+ if (rg_pstati(pm, CONVOLUTION) != PM_CONKERNEL)
+ call rg_pwrite (pm, imk, NULL)
+ }
+ } else {
+ if (verbose == YES) {
+ call printf (
+ "Error computing/reading kernel %s for image %s\n")
+ call pargstr (Memc[kernel])
+ call pargstr (Memc[image1])
+ }
+ }
+ stat = NO
+ }
+
+ # Convolve the image.
+ if (im2 != NULL && stat == NO) {
+ if (verbose == YES) {
+ if (rg_pstatp(pm, CONV) != NULL)
+ call printf (
+ "\tComputing matched image %s ...\n")
+ else
+ call printf (
+ "\tComputing matched image %s ...\n")
+ call pargstr (Memc[imtemp])
+ call pargstr (Memc[kernel])
+ }
+ if (rg_pstatp(pm, CONV) != NULL)
+ call rg_pconvolve (im1, im2, Memr[rg_pstatp(pm,CONV)],
+ rg_pstati(pm,KNX), rg_pstati(pm,KNY), boundary,
+ constant)
+ }
+
+ # Close up the images.
+ if (im2 != NULL) {
+ call imunmap (im2)
+ if (rg_pstatp(pm, CONV) == NULL)
+ call imdelete (Memc[image2])
+ else
+ call xt_delimtemp (Memc[image2], Memc[imtemp])
+ }
+ if (impsf != NULL)
+ call imunmap (impsf)
+ if (imk != NULL) {
+ call imunmap (imk)
+ if (rg_pstati (pm, CONVOLUTION) != PM_CONKERNEL &&
+ rg_pstatp(pm, CONV) == NULL)
+ call imdelete (Memc[kernel])
+ }
+ call imunmap (im1)
+
+ if (stat == YES)
+ break
+ newref = NO
+ }
+
+ # Close up the lists.
+ if (imr != NULL)
+ call imunmap (imr)
+
+ if (list2 != NULL)
+ call imtclose (list2)
+ if (listk != NULL)
+ call imtclose (listk)
+ if (psflist != NULL) {
+ if (rg_pstati (pm, CONVOLUTION) == PM_CONIMAGE)
+ call fntclsb (psflist)
+ else
+ call imtclose (psflist)
+ }
+ if (listr != NULL)
+ call imtclose (listr)
+ call imtclose (list1)
+
+ call rg_pfree (pm)
+
+ # Close up te graphics and the display.
+ if (gd != NULL)
+ call gclose (gd)
+ if (id != NULL)
+ call gclose (id)
+
+ call sfree (sp)
+end
+
+
+# RG_PTMPIMAGE -- Generate either a permanent image name using a user specified
+# prefix or temporary image name using a default prefix. Return NO if the
+# image is temporary or YES if it is permanent.
+
+int procedure rg_ptmpimage (image, prefix, tmp, name, maxch)
+
+char image[ARB] #I image name
+char prefix[ARB] #I user supplied prefix
+char tmp[ARB] #I user supplied temporary root
+char name[ARB] #O output name
+int maxch #I max number of chars
+
+int npref, ndir
+int fnldir(), rg_pimroot(), strlen()
+
+begin
+ npref = strlen (prefix)
+ ndir = fnldir (prefix, name, maxch)
+ if (npref == ndir) {
+ call mktemp (tmp, name[ndir+1], maxch)
+ return (NO)
+ } else {
+ call strcpy (prefix, name, npref)
+ if (rg_pimroot (image, name[npref+1], maxch) <= 0)
+ ;
+ return (YES)
+ }
+end
+
+
+# RG_PIMROOT -- Fetch the root image name minus the directory specification
+# and the section notation. The length of the root name is returned.
+
+int procedure rg_pimroot (image, root, maxch)
+
+char image[ARB] #I image specification
+char root[ARB] #O rootname
+int maxch #I maximum number of characters
+
+int nchars
+pointer sp, str
+int fnldir(), strlen()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ call imgimage (image, root, maxch)
+ nchars = fnldir (root, Memc[str], maxch)
+ call strcpy (root[nchars+1], root, maxch)
+
+ call sfree (sp)
+ return (strlen (root))
+end
diff --git a/pkg/images/immatch/src/wcsmatch/mkpkg b/pkg/images/immatch/src/wcsmatch/mkpkg
new file mode 100644
index 00000000..638ee1e8
--- /dev/null
+++ b/pkg/images/immatch/src/wcsmatch/mkpkg
@@ -0,0 +1,14 @@
+# Make the SKYXYMATCH / WCSXYMATCH / WCSCOPY tasks
+
+$checkout libpkg.a ../../../
+$update libpkg.a
+$checkin libpkg.a ../../../
+$exit
+
+libpkg.a:
+ rgmatchio.x wcsxymatch.h
+ t_skyxymatch.x <fset.h> <imhdr.h> <mwset.h> <math.h> \
+ <pkg/skywcs.h> wcsxymatch.h
+ t_wcscopy.x <imhdr.h> <mwset.h>
+ t_wcsxymatch.x <fset.h> <imhdr.h> <mwset.h> wcsxymatch.h
+ ;
diff --git a/pkg/images/immatch/src/wcsmatch/rgmatchio.x b/pkg/images/immatch/src/wcsmatch/rgmatchio.x
new file mode 100644
index 00000000..1a0de167
--- /dev/null
+++ b/pkg/images/immatch/src/wcsmatch/rgmatchio.x
@@ -0,0 +1,77 @@
+include "wcsxymatch.h"
+
+define DEF_BUFSIZE 200
+
+# RG_RDXY -- Read in the x and y coordinates from a file.
+
+int procedure rg_rdxy (fd, x, y, wcs, xcolumn, ycolumn, xunits, yunits)
+
+int fd #I the input file descriptor
+pointer x #U pointer to the x coordinates
+pointer y #U pointer to the y coordinates
+int wcs #I the world coordinate system
+int xcolumn #I column containing the x coordinate
+int ycolumn #I column containing the y coordinate
+int xunits #I the x coordinate units
+int yunits #I the y coordinate units
+
+double xval, yval
+int i, ip, bufsize, maxcols, npts
+pointer sp, str
+int fscan(), nscan(), ctod()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ bufsize = DEF_BUFSIZE
+ call malloc (x, bufsize, TY_DOUBLE)
+ call malloc (y, bufsize, TY_DOUBLE)
+ maxcols = max (xcolumn, ycolumn)
+
+ npts = 0
+ while (fscan(fd) != EOF) {
+
+ xval = INDEFD
+ yval = INDEFD
+ do i = 1, maxcols {
+ call gargwrd (Memc[str], SZ_FNAME)
+ if (i != nscan())
+ break
+ ip = 1
+ if (i == xcolumn) {
+ if (ctod (Memc[str], ip, xval) <= 0)
+ xval = INDEFD
+ } else if (i == ycolumn) {
+ if (ctod (Memc[str], ip, yval) <= 0)
+ yval = INDEFD
+ }
+ }
+ if (IS_INDEFD(xval) || IS_INDEFD(yval))
+ next
+
+ Memd[x+npts] = xval
+ Memd[y+npts] = yval
+ npts = npts + 1
+ if (npts >= bufsize) {
+ bufsize = bufsize + DEF_BUFSIZE
+ call realloc (x, bufsize, TY_DOUBLE)
+ call realloc (y, bufsize, TY_DOUBLE)
+ }
+ }
+
+ # Convert the coordinates if necessary.
+ switch (wcs) {
+ case RG_WORLD:
+ if (xunits == RG_UHOURS)
+ call amulkd (Memd[x], 15.0d0, Memd[x], npts)
+ if (yunits == RG_UHOURS)
+ call amulkd (Memd[y], 15.0d0, Memd[y], npts)
+ default:
+ ;
+ }
+
+ call sfree (sp)
+
+ return (npts)
+end
diff --git a/pkg/images/immatch/src/wcsmatch/t_skyxymatch.x b/pkg/images/immatch/src/wcsmatch/t_skyxymatch.x
new file mode 100644
index 00000000..533d36a8
--- /dev/null
+++ b/pkg/images/immatch/src/wcsmatch/t_skyxymatch.x
@@ -0,0 +1,690 @@
+include <fset.h>
+include <imhdr.h>
+include <mwset.h>
+include <math.h>
+include <pkg/skywcs.h>
+include "wcsxymatch.h"
+
+# T_SKYXYMATCH -- Compute a list of the tie points required to register an
+# image to a reference image using WCS information in the image headers and
+# the celestial coordinate transformation routines.
+
+procedure t_skyxymatch()
+
+bool verbose
+double xmin, xmax, ymin, ymax, x1, x2, y1, y2
+int ilist, rlist, olist, clist, cfd, ofd
+int nx, ny, wcs, min_sigdigits, xcolumn, ycolumn, xunits, yunits
+int rstat, stat, npts
+pointer sp, refimage, image, xformat, yformat, rxformat, ryformat
+pointer rwxformat, rwyformat, txformat, tyformat, twxformat, twyformat, str
+pointer imr, im, mwr, mw, coor, coo, ctr, ct
+pointer rxl, ryl, rxw, ryw, trxw, tryw, ixl, iyl
+
+bool clgetb(), streq()
+double clgetd()
+int imtopen(), fntopnb(), clgeti(), clgwrd(), strdic(), imtlen()
+int fntlenb(), imtgetim(), fntgfnb(), open(), mw_stati(), sk_decim()
+int rg_rdxy(), rg_xytoxy(), sk_stati()
+pointer immap()
+errchk mw_gwattrs()
+
+begin
+ # Get some temporary working space.
+ call smark (sp)
+ call salloc (refimage, SZ_FNAME, TY_CHAR)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (xformat, SZ_FNAME, TY_CHAR)
+ call salloc (yformat, SZ_FNAME, TY_CHAR)
+ call salloc (rwxformat, SZ_FNAME, TY_CHAR)
+ call salloc (rwyformat, SZ_FNAME, TY_CHAR)
+ call salloc (rxformat, SZ_FNAME, TY_CHAR)
+ call salloc (ryformat, SZ_FNAME, TY_CHAR)
+ call salloc (twxformat, SZ_FNAME, TY_CHAR)
+ call salloc (twyformat, SZ_FNAME, TY_CHAR)
+ call salloc (txformat, SZ_FNAME, TY_CHAR)
+ call salloc (tyformat, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get the input image and output file lists.
+ call clgstr ("input", Memc[str], SZ_FNAME)
+ ilist = imtopen (Memc[str])
+ call clgstr ("reference", Memc[str], SZ_FNAME)
+ rlist = imtopen (Memc[str])
+ call clgstr ("output", Memc[str], SZ_FNAME)
+ if (Memc[str] == EOS)
+ call strcpy ("STDOUT", Memc[str], SZ_FNAME)
+ olist = fntopnb (Memc[str], NO)
+
+ # Determine the source of the input coordinates.
+ call clgstr ("coords", Memc[str], SZ_FNAME)
+ if (streq (Memc[str], "grid")) {
+ clist = NULL
+ xmin = clgetd ("xmin")
+ xmax = clgetd ("xmax")
+ ymin = clgetd ("ymin")
+ ymax = clgetd ("ymax")
+ nx = clgeti ("nx")
+ ny = clgeti ("ny")
+ wcs = clgwrd ("wcs", Memc[str], SZ_FNAME, RG_WCSLIST)
+ } else {
+ clist = fntopnb (Memc[str], NO)
+ xmin = INDEFD
+ xmax = INDEFD
+ ymin = INDEFD
+ ymax = INDEFD
+ nx = clgeti ("nx")
+ ny = clgeti ("ny")
+ wcs = clgwrd ("wcs", Memc[str], SZ_FNAME, RG_WCSLIST)
+ xcolumn = clgeti ("xcolumn")
+ ycolumn = clgeti ("ycolumn")
+ call clgstr ("xunits", Memc[str], SZ_FNAME)
+ xunits = strdic (Memc[str], Memc[str], SZ_FNAME, RG_UNITLIST)
+ if (xunits <= 0)
+ xunits = RG_UNATIVE
+ call clgstr ("yunits", Memc[str], SZ_FNAME)
+ yunits = strdic (Memc[str], Memc[str], SZ_FNAME, RG_UNITLIST)
+ if (yunits <= 0)
+ yunits = RG_UNATIVE
+ }
+
+ # Get the output coordinate formatting information.
+ call clgstr ("xformat", Memc[xformat], SZ_FNAME)
+ call clgstr ("yformat", Memc[yformat], SZ_FNAME)
+ call clgstr ("rwxformat", Memc[rxformat], SZ_FNAME)
+ call clgstr ("rwyformat", Memc[ryformat], SZ_FNAME)
+ call clgstr ("wxformat", Memc[txformat], SZ_FNAME)
+ call clgstr ("wyformat", Memc[tyformat], SZ_FNAME)
+ min_sigdigits = clgeti ("min_sigdigits")
+
+ # Get remaining parameters.
+ verbose = clgetb ("verbose")
+
+ # Check the formatting of the reference and input logical coordinates.
+ if (Memc[xformat] == EOS) {
+ call sprintf (Memc[xformat], SZ_FNAME, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ }
+ if (Memc[yformat] == EOS) {
+ call sprintf (Memc[yformat], SZ_FNAME, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ }
+
+ # Check the reference image list length.
+ if (imtlen (rlist) <= 0)
+ call error (0, "The reference image list is empty.")
+ if (imtlen(rlist) > 1 && imtlen(rlist) != imtlen(ilist))
+ call error (0,
+ "The number of reference and input images is not the same.")
+
+ # Check the output coordinate file length.
+ if (fntlenb(olist) > 1 && fntlenb(olist) != imtlen(ilist))
+ call error (0,
+ "The number of output coords files and input images is not the same.")
+
+ # Check the reference coordinate list length.
+ if (clist != NULL) {
+ if (fntlenb (clist) != imtlen (rlist))
+ call error (0,
+ "The number of reference coords files and images are not the same")
+ }
+
+ # Initialize the reference image and coordinate list pointers.
+ imr = NULL
+ cfd = NULL
+
+ # Loop over the input images.
+ while (imtgetim (ilist, Memc[image], SZ_FNAME) != EOF) {
+
+ # Open the reference image and reference coordinate file and
+ # compute the logical and world reference coordinates.
+ if (imtgetim (rlist, Memc[refimage], SZ_FNAME) != EOF) {
+
+ # Open the reference image.
+ if (imr != NULL) {
+ call mfree (rxl, TY_DOUBLE)
+ call mfree (ryl, TY_DOUBLE)
+ call mfree (rxw, TY_DOUBLE)
+ call mfree (ryw, TY_DOUBLE)
+ call mfree (trxw, TY_DOUBLE)
+ call mfree (tryw, TY_DOUBLE)
+ call mfree (ixl, TY_DOUBLE)
+ call mfree (iyl, TY_DOUBLE)
+ if (mwr != NULL)
+ call mw_close (mwr)
+ if (coor != NULL)
+ #call mfree (coor, TY_STRUCT)
+ call sk_close (coor)
+ call imunmap (imr)
+ }
+ imr = immap (Memc[refimage], READ_ONLY, 0)
+ if (IM_NDIM(imr) > 2)
+ call error (0, "The reference image must be 1D or 2D")
+
+ # Open the reference image wcs.
+ rstat = sk_decim (imr, "logical", mwr, coor)
+
+ # Check that the wcs dimensions are rational.
+ if (mwr != NULL) {
+ if (mw_stati(mwr, MW_NPHYSDIM) < IM_NDIM(imr) ||
+ mw_stati (mwr, MW_NDIM) != IM_NDIM(imr)) {
+ call mw_close (mwr)
+ mwr = NULL
+ }
+ }
+
+ # Compute the x limits of the logical reference coordinates.
+ if (IS_INDEFD(xmin))
+ x1 = 1.0d0
+ else
+ x1 = max (1.0d0, min (xmin, double(IM_LEN(imr,1))))
+ if (IS_INDEFD(xmax))
+ x2 = double(IM_LEN(imr,1))
+ else
+ x2 = max (1.0d0, min (xmax, double(IM_LEN(imr,1))))
+
+ # Compute the y limits of the logical reference coordinates.
+ if (IM_NDIM(imr) == 1)
+ y1 = 1.0d0
+ else if (IS_INDEFD(ymin))
+ y1 = 1.0d0
+ else
+ y1 = max (1.0d0, min (ymin, double(IM_LEN(imr,2))))
+ if (IM_NDIM(imr) == 1)
+ y2 = 1.0d0
+ else if (IS_INDEFD(ymax))
+ y2 = double(IM_LEN(imr,2))
+ else
+ y2 = max (1.0d0, min (ymax, double(IM_LEN(imr,2))))
+
+ # Compute the reference logical and world coordinates.
+ if (clist != NULL) {
+
+ if (cfd != NULL)
+ call close (cfd)
+
+ if (fntgfnb (clist, Memc[str], SZ_FNAME) != EOF) {
+ cfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ npts = rg_rdxy (cfd, rxw, ryw, wcs, xcolumn, ycolumn,
+ xunits, yunits)
+ call malloc (trxw, npts, TY_DOUBLE)
+ call malloc (tryw, npts, TY_DOUBLE)
+ call malloc (rxl, npts, TY_DOUBLE)
+ call malloc (ryl, npts, TY_DOUBLE)
+ call malloc (ixl, npts, TY_DOUBLE)
+ call malloc (iyl, npts, TY_DOUBLE)
+ if (wcs == RG_WORLD)
+ ctr = rg_xytoxy (mwr, Memd[rxw], Memd[ryw],
+ Memd[rxl], Memd[ryl], npts, "world", "logical",
+ 1, 2)
+ else
+ ctr = rg_xytoxy (mwr, Memd[rxw], Memd[ryw],
+ Memd[rxl], Memd[ryl], npts, "physical",
+ "logical", 1, 2)
+ }
+
+ } else {
+
+ if (IM_NDIM(imr) == 1)
+ npts = nx
+ else
+ npts = nx * ny
+ call malloc (rxl, npts, TY_DOUBLE)
+ call malloc (ryl, npts, TY_DOUBLE)
+ call malloc (rxw, npts, TY_DOUBLE)
+ call malloc (ryw, npts, TY_DOUBLE)
+ call malloc (trxw, npts, TY_DOUBLE)
+ call malloc (tryw, npts, TY_DOUBLE)
+ call malloc (ixl, npts, TY_DOUBLE)
+ call malloc (iyl, npts, TY_DOUBLE)
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, x1, x2,
+ y1, y2)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, x1, x2,
+ y1, y2)
+ if (wcs == RG_WORLD)
+ ctr = rg_xytoxy (mwr, Memd[rxl], Memd[ryl], Memd[rxw],
+ Memd[ryw], npts, "logical", "world", 1, 2)
+ else
+ ctr = rg_xytoxy (mwr, Memd[rxl], Memd[ryl], Memd[rxw],
+ Memd[ryw], npts, "logical", "physical", 1, 2)
+
+ }
+ }
+
+ # Open the input image.
+ im = immap (Memc[image], READ_ONLY, 0)
+ if (IM_NDIM(im) > 2)
+ call error (0, "The input image must be 1D or 2D")
+ if (IM_NDIM(im) != IM_NDIM(imr))
+ call error (0,
+ "The input image must have same dimensionality as reference image")
+
+ # Open the input wcs.
+ stat = sk_decim (im, "logical", mw, coo)
+ if (mw != NULL) {
+ if (mw_stati(mw, MW_NPHYSDIM) < IM_NDIM(im) ||
+ mw_stati (mw, MW_NDIM) != IM_NDIM(im)) {
+ call mw_close (mw)
+ mw = NULL
+ }
+ }
+
+ # Open the output file.
+ if (fntgfnb (olist, Memc[str], SZ_FNAME) != EOF)
+ ofd = open (Memc[str], NEW_FILE, TEXT_FILE)
+
+ # Print information about the reference and input coordinate
+ # systems and the reference and input files to the output
+ # file
+ if (ofd == STDOUT)
+ call fseti (ofd, F_FLUSHNL, YES)
+ if (streq (Memc[str], "STDOUT") || ofd == STDOUT)
+ call fseti (ofd, F_FLUSHNL, YES)
+ call fprintf (ofd, "\n")
+ call fprintf (ofd,
+ "# Reference image: %s Input image: %s\n# Coords: %s")
+ call pargstr (Memc[refimage])
+ call pargstr (Memc[image])
+ if (clist == NULL) {
+ call pargstr ("grid")
+ call fprintf (ofd, " Wcs: logical\n")
+ } else {
+ call fstats (cfd, F_FILENAME, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call fprintf (ofd, " Wcs: %s\n")
+ switch (wcs) {
+ case RG_PHYSICAL:
+ call pargstr ("physical")
+ case RG_WORLD:
+ call pargstr ("world")
+ default:
+ call pargstr ("world")
+ }
+ }
+ if (rstat == ERR)
+ call fprintf (ofd,
+ "# Error decoding the reference coordinate system\n")
+ call sk_iiwrite (ofd, "Refsystem", Memc[refimage], mwr, coor)
+ if (stat == ERR)
+ call fprintf (ofd,
+ "# Error decoding the input coordinate system\n")
+ call sk_iiwrite (ofd, "Insystem", Memc[image], mw, coo)
+
+ # Print information about the reference and input coordinate
+ # systems and the reference and input files to the standard
+ # output.
+ if (verbose && ofd != STDOUT) {
+ call printf ("\n")
+ call printf (
+ "Reference image: %s Input image: %s\n Coords: %s")
+ call pargstr (Memc[refimage])
+ call pargstr (Memc[image])
+ if (clist == NULL) {
+ call pargstr ("grid")
+ call printf (" Wcs: logical\n")
+ } else {
+ call fstats (cfd, F_FILENAME, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call printf (" Wcs: %s\n")
+ switch (wcs) {
+ case RG_PHYSICAL:
+ call pargstr ("physical")
+ case RG_WORLD:
+ call pargstr ("world")
+ default:
+ call pargstr ("world")
+ }
+ }
+ if (rstat == ERR)
+ call printf (
+ "Error decoding the rference coordinate system\n")
+ call sk_iiprint ("Refsystem", Memc[refimage], mwr, coor)
+ if (stat == ERR)
+ call printf (
+ "Error decoding the input coordinate system\n")
+ call sk_iiprint ("Insystem", Memc[image], mw, coo)
+ call printf ("\n")
+ }
+
+ # Set the reference and input coordinate formats.
+ if (Memc[rxformat] == EOS)
+ call rg_ssetfmt (mwr, wcs, sk_stati(coor, S_XLAX),
+ min_sigdigits, Memc[rwxformat], SZ_FNAME)
+ else
+ call strcpy (Memc[rxformat], Memc[rwxformat], SZ_FNAME)
+
+ if (Memc[txformat] == EOS)
+ call rg_ssetfmt (mw, wcs, sk_stati(coo, S_XLAX),
+ min_sigdigits, Memc[twxformat], SZ_FNAME)
+ else
+ call strcpy (Memc[txformat], Memc[twxformat], SZ_FNAME)
+ if (Memc[ryformat] == EOS)
+ call rg_ssetfmt (mwr, wcs, sk_stati(coor, S_YLAX),
+ min_sigdigits, Memc[rwyformat], SZ_FNAME)
+ else
+ call strcpy (Memc[ryformat], Memc[rwyformat], SZ_FNAME)
+ if (Memc[tyformat] == EOS)
+ call rg_ssetfmt (mw, wcs, sk_stati(coo, S_YLAX),
+ min_sigdigits, Memc[twyformat], SZ_FNAME)
+ else
+ call strcpy (Memc[tyformat], Memc[twyformat], SZ_FNAME)
+
+
+ # Compute the output coordinates issuing a warning if the
+ # axes types are not compatable.
+ if (mwr == NULL || rstat == ERR) {
+ call fprintf (ofd,
+ "# \tWarning: error decoding reference image wcs\n")
+ if (verbose && ofd != STDOUT)
+ call printf (
+ "\tWarning: error decoding reference image wcs\n")
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ if (clist == NULL) {
+ call amovd (Memd[rxl], Memd[rxw], npts)
+ call amovd (Memd[ryl], Memd[ryw], npts)
+ call amovd (Memd[rxl], Memd[trxw], npts)
+ call amovd (Memd[ryl], Memd[tryw], npts)
+ }
+ ct = NULL
+ } else if (ctr == NULL) {
+ call fprintf (ofd, "# \tWarning: Unable to compute reference \
+logical <-> world transform\n")
+ if (verbose && ofd != STDOUT)
+ call printf ("\tWarning: Unable to compute reference \
+logical <-> world transform\n")
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ if (clist == NULL) {
+ call amovd (Memd[rxl], Memd[rxw], npts)
+ call amovd (Memd[ryl], Memd[ryw], npts)
+ call amovd (Memd[rxl], Memd[trxw], npts)
+ call amovd (Memd[ryl], Memd[tryw], npts)
+ }
+ ct = NULL
+ } else if (mw == NULL || stat == ERR) {
+ call fprintf (ofd,
+ "# \tWarning: error decoding input image wcs\n")
+ if (verbose && ofd != STDOUT)
+ call printf ("\tWarning: error decoding input image wcs\n")
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ call amovd (Memd[rxw], Memd[trxw], npts)
+ call amovd (Memd[ryw], Memd[tryw], npts)
+ ct = NULL
+ } else {
+ # Check axis status.
+ if (wcs == RG_PHYSICAL) {
+ ct = rg_xytoxy (mw, Memd[rxw], Memd[ryw], Memd[ixl],
+ Memd[iyl], npts, "physical", "logical", 1, 2)
+ call amovd (Memd[rxw], Memd[trxw], npts)
+ call amovd (Memd[ryw], Memd[tryw], npts)
+ if (ct == NULL) {
+ call fprintf (ofd,
+ "# \tWarning: Unable to compute image physical -> \
+logical transform\n")
+ if (verbose && ofd != STDOUT)
+ call printf (
+ "\tWarning: Unable to compute image physical \
+-> logical transform\n")
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0,
+ double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ }
+ } else {
+ call rg_lltransform (coor, coo, Memd[rxw], Memd[ryw],
+ Memd[trxw], Memd[tryw], npts)
+ if ((sk_stati (coor, S_PLNGAX) < sk_stati(coor,
+ S_PLATAX)) && (sk_stati (coo,S_PLNGAX) <
+ sk_stati(coo, S_PLATAX)))
+ ct = rg_xytoxy (mw, Memd[trxw], Memd[tryw], Memd[ixl],
+ Memd[iyl], npts, "world", "logical", 1, 2)
+ else if ((sk_stati (coor, S_PLNGAX) > sk_stati(coor,
+ S_PLATAX)) && (sk_stati (coo,S_PLNGAX) >
+ sk_stati(coo, S_PLATAX)))
+ ct = rg_xytoxy (mw, Memd[trxw], Memd[tryw], Memd[ixl],
+ Memd[iyl], npts, "world", "logical", 1, 2)
+ else
+ ct = rg_xytoxy (mw, Memd[tryw], Memd[trxw], Memd[ixl],
+ Memd[iyl], npts, "world", "logical", 1, 2)
+ if (ct == NULL) {
+ call fprintf (ofd,
+ "# \tWarning: Unable to compute image world -> \
+logical transform\n")
+ if (verbose && ofd != STDOUT)
+ call printf (
+ "\tWarning: Unable to compute image world -> \
+logical transform\n")
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0,
+ double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ }
+ }
+ }
+
+ # Write out the results.
+ if ((sk_stati (coor, S_PLNGAX) < sk_stati(coor, S_PLATAX)) &&
+ (sk_stati (coo,S_PLNGAX) < sk_stati(coo, S_PLATAX)))
+ call rg_swcoords (ofd, Memd[rxl], Memd[ryl], Memd[ixl],
+ Memd[iyl], Memd[rxw], Memd[ryw], Memd[trxw], Memd[tryw],
+ npts, Memc[xformat], Memc[yformat], Memc[rwxformat],
+ Memc[rwyformat], Memc[twxformat], Memc[twyformat])
+ else if ((sk_stati (coor, S_PLNGAX) > sk_stati(coor,
+ S_PLATAX)) && (sk_stati (coo,S_PLNGAX) > sk_stati(coo,
+ S_PLATAX)))
+ call rg_swcoords (ofd, Memd[rxl], Memd[ryl], Memd[ixl],
+ Memd[iyl], Memd[rxw], Memd[ryw], Memd[trxw], Memd[tryw],
+ npts, Memc[xformat], Memc[yformat], Memc[rwxformat],
+ Memc[rwyformat], Memc[twxformat], Memc[twyformat])
+ else
+ call rg_swcoords (ofd, Memd[rxl], Memd[ryl], Memd[ixl],
+ Memd[iyl], Memd[rxw], Memd[ryw], Memd[tryw], Memd[trxw],
+ npts, Memc[xformat], Memc[yformat], Memc[rwxformat],
+ Memc[rwyformat], Memc[twxformat], Memc[twyformat])
+
+ # Close the input image and its wcs.
+ if (mw != NULL)
+ call mw_close (mw)
+ if (coo != NULL)
+ #call mfree (coo, TY_STRUCT)
+ call sk_close (coo)
+ call imunmap (im)
+
+ # Close the output coordinate file if it is not going to
+ # be appended to.
+ if (fntlenb(olist) == imtlen(ilist))
+ call close (ofd)
+ }
+
+ if (imr != NULL) {
+ call mfree (rxl, TY_DOUBLE)
+ call mfree (ryl, TY_DOUBLE)
+ call mfree (rxw, TY_DOUBLE)
+ call mfree (ryw, TY_DOUBLE)
+ call mfree (trxw, TY_DOUBLE)
+ call mfree (tryw, TY_DOUBLE)
+ call mfree (ixl, TY_DOUBLE)
+ call mfree (iyl, TY_DOUBLE)
+ if (mwr != NULL)
+ call mw_close (mwr)
+ if (coor != NULL)
+ #call mfree (coor, TY_STRUCT)
+ call sk_close (coor)
+ call imunmap (imr)
+ }
+ if (cfd != NULL)
+ call close (cfd)
+ if (fntlenb(olist) < imtlen(ilist))
+ call close (ofd)
+ if (ilist != NULL)
+ call imtclose (ilist)
+ if (rlist != NULL)
+ call imtclose (rlist)
+ if (olist != NULL)
+ call fntclsb (olist)
+ if (clist != NULL)
+ call fntclsb (clist)
+
+ call sfree (sp)
+end
+
+
+# RG_SSETFMT -- Procedure to set the appropriate default format.
+
+procedure rg_ssetfmt (mw, wcs, laxno, min_sigdigits, wformat, maxch)
+
+pointer mw #I pointer to the image wcs
+int wcs #I the input wcs type
+int laxno #I the physical axis number
+int min_sigdigits #I the minmum number of significant digits
+char wformat[ARB] #O the output format string
+int maxch #I the maximum size of the output format string
+
+pointer sp, str
+bool streq()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ if (mw == NULL) {
+ call sprintf (wformat, maxch, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ } else if (wcs == RG_PHYSICAL) {
+ call strcpy ("%10.3f", wformat, maxch)
+ } else {
+ iferr {
+ call mw_gwattrs (mw, laxno, "format", wformat, maxch)
+ } then {
+ iferr {
+ call mw_gwattrs (mw, laxno, "axtype", Memc[str], SZ_FNAME)
+ } then {
+ call sprintf (wformat, maxch, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ } else {
+ if (streq (Memc[str], "ra"))
+ call strcpy ("%12.2H", wformat, maxch)
+ else if (streq (Memc[str], "dec"))
+ call strcpy ("%11.1h", wformat, maxch)
+ else if (streq (Memc[str+1], "lon"))
+ call strcpy ("%11.1h", wformat, maxch)
+ else if (streq (Memc[str+1], "lat"))
+ call strcpy ("%11.1h", wformat, maxch)
+ else {
+ call sprintf (wformat, maxch, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ }
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_SWCOORDS -- Write out the reference and input logical coordinates of the
+# tie points and the reference world coordinates.
+
+procedure rg_swcoords (ofd, xref, yref, xin, yin, wxref, wyref, twxref, twyref,
+ npts, xformat, yformat, wxformat, wyformat, twxformat, twyformat)
+
+int ofd #I the output file descriptor
+double xref[ARB] #I the reference logical x coordinates
+double yref[ARB] #I the reference logical y coordinates
+double xin[ARB] #I the input logical x coordinates
+double yin[ARB] #I the input logical y coordinates
+double wxref[ARB] #I the reference world x coordinates
+double wyref[ARB] #I the reference world y coordinates
+double twxref[ARB] #I the input world x coordinates
+double twyref[ARB] #I the input world y coordinates
+int npts #I the number of input points
+char xformat[ARB] #I the logical x coordinates format
+char yformat[ARB] #I the logical y coordinates format
+char wxformat[ARB] #I the reference world x coordinates format
+char wyformat[ARB] #I the reference world y coordinates format
+char twxformat[ARB] #I the input world x coordinates format
+char twyformat[ARB] #I the input world y coordinates format
+
+int i
+pointer sp, fmtstr
+
+begin
+ call smark (sp)
+ call salloc (fmtstr, SZ_LINE, TY_CHAR)
+
+ # Write the column descriptions.
+ call fprintf (ofd,
+ "# \tColumn 1: reference logical x coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 2: reference logical y coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 3: input logical x coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 4: input logical y coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 5: reference world x coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 6: reference world y coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 7: input world x coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 8: input world y coordinate\n")
+ call fprintf (ofd, "\n")
+
+ call sprintf (Memc[fmtstr], SZ_LINE,
+ "%s %s %s %s %s %s %s %s\n")
+ call pargstr (xformat)
+ call pargstr (yformat)
+ call pargstr (xformat)
+ call pargstr (yformat)
+ call pargstr (wxformat)
+ call pargstr (wyformat)
+ call pargstr (twxformat)
+ call pargstr (twyformat)
+
+ do i = 1, npts {
+ call fprintf (ofd, Memc[fmtstr])
+ call pargd (xref[i])
+ call pargd (yref[i])
+ call pargd (xin[i])
+ call pargd (yin[i])
+ call pargd (wxref[i])
+ call pargd (wyref[i])
+ call pargd (twxref[i])
+ call pargd (twyref[i])
+ }
+
+ call sfree (sp)
+end
+
diff --git a/pkg/images/immatch/src/wcsmatch/t_wcscopy.x b/pkg/images/immatch/src/wcsmatch/t_wcscopy.x
new file mode 100644
index 00000000..6d15e5c8
--- /dev/null
+++ b/pkg/images/immatch/src/wcsmatch/t_wcscopy.x
@@ -0,0 +1,199 @@
+include <imhdr.h>
+include <mwset.h>
+
+# T_WCSCOPY -- Copy the world coordinate system of a reference image to
+# the world coordinate system of an input image.
+
+procedure t_wcscopy()
+
+bool verbose
+int ilist, rlist
+pointer sp, image, refimage, value, str, imr, mwr, im
+real rval
+double dval
+bool clgetb()
+int imtopen(), imtlen(), imtgetim()
+#int mw_stati(), rg_samesize()
+pointer immap(), mw_openim()
+real imgetr()
+double imgetd()
+errchk mw_openim(), imgstr(), imgetr(), imgetd(), imdelf()
+
+begin
+ # Get some temporary working space.
+ call smark (sp)
+ call salloc (refimage, SZ_FNAME, TY_CHAR)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (value, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get the input image and reference image lists.
+ call clgstr ("images", Memc[str], SZ_FNAME)
+ ilist = imtopen (Memc[str])
+ call clgstr ("refimages", Memc[str], SZ_FNAME)
+ rlist = imtopen (Memc[str])
+ verbose = clgetb ("verbose")
+
+ # Check the reference image list length.
+ if (imtlen (rlist) <= 0)
+ call error (0, "The reference image list is empty.")
+ if (imtlen(rlist) > 1 && imtlen(rlist) != imtlen(ilist))
+ call error (0,
+ "The number of reference and input images is not the same.")
+
+ # Initialize the reference image and coordinate list pointers.
+ imr = NULL
+
+ # Loop over the input images.
+ while (imtgetim (ilist, Memc[image], SZ_FNAME) != EOF) {
+
+ # Open the reference image and reference coordinate file and
+ # compute the logical and world reference coordinates.
+ if (imtgetim (rlist, Memc[refimage], SZ_FNAME) != EOF) {
+
+ # Open the reference image.
+ if (imr != NULL) {
+ if (mwr != NULL)
+ call mw_close (mwr)
+ call imunmap (imr)
+ }
+ imr = immap (Memc[refimage], READ_ONLY, 0)
+
+ # Open the reference image wcs.
+ iferr (mwr = mw_openim (imr))
+ mwr = NULL
+
+ # Check that the wcs dimensions are rational.
+# if (mwr != NULL) {
+# if (mw_stati(mwr, MW_NPHYSDIM) < IM_NDIM(imr)) {
+# call mw_close (mwr)
+# mwr = NULL
+# }
+# }
+ }
+
+ # Print message about progress of task
+ if (verbose) {
+ call printf ("Copying wcs from image %s to image %s\n")
+ call pargstr (Memc[refimage])
+ call pargstr (Memc[image])
+ }
+
+ # Remove any image section and open the input image.
+ call imgimage (Memc[image], Memc[image], SZ_FNAME)
+ iferr (im = immap (Memc[image], READ_WRITE, 0)) {
+ im = immap (Memc[image], NEW_IMAGE, 0)
+ IM_NDIM(im) = 0
+ }
+
+ # Test for valid wcs.
+ if (mwr == NULL) {
+ if (verbose) {
+ call printf (
+ "\tError: cannot read wcs for reference image %s\n")
+ call pargstr (Memc[refimage])
+ }
+# } else if (IM_NDIM(im) != IM_NDIM(imr)) {
+# if (verbose) {
+# call printf (
+# "\tError: %s and %s have different number of dimensions\n")
+# call pargstr (Memc[image])
+# call pargstr (Memc[refimage])
+# }
+ } else {
+# if (rg_samesize (imr, im) == NO) {
+# if (verbose) {
+# call printf (
+# "\tWarning: images %s and %s have different sizes\n")
+# call pargstr (Memc[image])
+# call pargstr (Memc[refimage])
+# }
+# }
+ #mw = mw_open (NULL, mw_stati (mwr,MW_NPHYSDIM))
+ #call mw_loadim (mw, imr)
+ #call mw_saveim (mw, im)
+ #call mw_close (mw)
+ call mw_saveim (mwr, im)
+
+ # Copy the RADECSYS keyword to the input image header.
+ ifnoerr {
+ call imgstr (imr, "RADECSYS", Memc[value], SZ_FNAME)
+ } then {
+ call imastr (im, "RADECSYS", Memc[value])
+ } else {
+ iferr (call imdelf (im, "RADECSYS"))
+ ;
+ }
+
+ # Copy the EQUINOX or EPOCH keyword to the input image header
+ # EQUINOX keyword.
+ ifnoerr {
+ rval = imgetr (imr, "EQUINOX")
+ } then {
+ call imaddr (im, "EQUINOX", rval)
+ iferr (call imdelf (im, "EPOCH"))
+ ;
+ } else {
+ ifnoerr {
+ rval = imgetr (imr, "EPOCH")
+ } then {
+ call imaddr (im, "EQUINOX", rval)
+ iferr (call imdelf (im, "EPOCH"))
+ ;
+ } else {
+ iferr (call imdelf (im, "EQUINOX"))
+ ;
+ iferr (call imdelf (im, "EPOCH"))
+ ;
+ }
+ }
+
+ # Copy the MJD-WCSkeyword to the input image header.
+ ifnoerr {
+ dval = imgetd (imr, "MJD-WCS")
+ } then {
+ call imaddd (im, "MJD-WCS", dval)
+ } else {
+ iferr (call imdelf (im, "MJD-WCS"))
+ ;
+ }
+ }
+
+ # Close the input image.
+ call imunmap (im)
+
+ }
+
+ if (imr != NULL) {
+ if (mwr != NULL)
+ call mw_close (mwr)
+ call imunmap (imr)
+ }
+
+ if (ilist != NULL)
+ call imtclose (ilist)
+ if (rlist != NULL)
+ call imtclose (rlist)
+
+ call sfree (sp)
+end
+
+
+# RG_SAMESIZE -- Determine whether two images of the same dimension are
+# the same size.
+
+int procedure rg_samesize (im1, im2)
+
+pointer im1 #I the first image descriptor
+pointer im2 #I the second image descriptor
+
+int i, stat
+
+begin
+ stat = YES
+ do i = 1, IM_NDIM(im1) {
+ if (IM_LEN(im1,i) != IM_LEN(im2,i))
+ return (NO)
+ }
+ return (stat)
+end
diff --git a/pkg/images/immatch/src/wcsmatch/t_wcsxymatch.x b/pkg/images/immatch/src/wcsmatch/t_wcsxymatch.x
new file mode 100644
index 00000000..503bc7f3
--- /dev/null
+++ b/pkg/images/immatch/src/wcsmatch/t_wcsxymatch.x
@@ -0,0 +1,787 @@
+include <fset.h>
+include <imhdr.h>
+include <mwset.h>
+include "wcsxymatch.h"
+
+# T_WCSXYMATCH -- Compute a list of the tie points required to register an
+# image to a reference image using WCS information in the image headers.
+
+procedure t_wcsxymatch()
+
+bool verbose, transpose
+double xmin, xmax, ymin, ymax, x1, x2, y1, y2
+int ilist, rlist, olist, clist, cfd, ofd
+int nx, ny, npts, wcs, xcolumn, ycolumn
+int xunits, yunits, min_sigdigits, axstat, projstat
+pointer sp, refimage, image, xformat, yformat, rxformat, ryformat
+pointer wxformat, wyformat, str, paxno, rlaxno, laxno
+pointer im, imr, mw, mwr, rxl, ryl, rxw, ryw, ixl, iyl, ctr, ct
+
+bool clgetb(), streq()
+double clgetd()
+int imtopen(), fntopnb(), imtlen(), fntlenb(), imtgetim(), open(), clgeti()
+int clgwrd(), rg_rdxy(), fntgfnb(), rg_axstat(), rg_projstat(), mw_stati()
+int strdic()
+pointer immap(), mw_openim(), rg_xytoxy()
+errchk mw_openim(), mw_gwattrs()
+
+begin
+ # Get some temporary working space.
+ call smark (sp)
+ call salloc (refimage, SZ_FNAME, TY_CHAR)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (xformat, SZ_FNAME, TY_CHAR)
+ call salloc (yformat, SZ_FNAME, TY_CHAR)
+ call salloc (wxformat, SZ_FNAME, TY_CHAR)
+ call salloc (wyformat, SZ_FNAME, TY_CHAR)
+ call salloc (rxformat, SZ_FNAME, TY_CHAR)
+ call salloc (ryformat, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ call salloc (paxno, IM_MAXDIM, TY_INT)
+ call salloc (rlaxno, IM_MAXDIM, TY_INT)
+ call salloc (laxno, IM_MAXDIM, TY_INT)
+
+ # Get the input image and output file lists.
+ call clgstr ("input", Memc[str], SZ_FNAME)
+ ilist = imtopen (Memc[str])
+ call clgstr ("reference", Memc[str], SZ_FNAME)
+ rlist = imtopen (Memc[str])
+ call clgstr ("output", Memc[str], SZ_FNAME)
+ if (Memc[str] == EOS)
+ call strcpy ("STDOUT", Memc[str], SZ_FNAME)
+ olist = fntopnb (Memc[str], NO)
+
+ # Determine the source of the input coordinates.
+ call clgstr ("coords", Memc[str], SZ_FNAME)
+ if (streq (Memc[str], "grid")) {
+ clist = NULL
+ xmin = clgetd ("xmin")
+ xmax = clgetd ("xmax")
+ ymin = clgetd ("ymin")
+ ymax = clgetd ("ymax")
+ nx = clgeti ("nx")
+ ny = clgeti ("ny")
+ wcs = clgwrd ("wcs", Memc[str], SZ_FNAME, RG_WCSLIST)
+ } else {
+ clist = fntopnb (Memc[str], NO)
+ xmin = INDEFD
+ xmax = INDEFD
+ ymin = INDEFD
+ ymax = INDEFD
+ nx = clgeti ("nx")
+ ny = clgeti ("ny")
+ wcs = clgwrd ("wcs", Memc[str], SZ_FNAME, RG_WCSLIST)
+ xcolumn = clgeti ("xcolumn")
+ ycolumn = clgeti ("ycolumn")
+ call clgstr ("xunits", Memc[str], SZ_FNAME)
+ xunits = strdic (Memc[str], Memc[str], SZ_FNAME, RG_UNITLIST)
+ if (xunits <= 0)
+ xunits = RG_UNATIVE
+ call clgstr ("yunits", Memc[str], SZ_FNAME)
+ yunits = strdic (Memc[str], Memc[str], SZ_FNAME, RG_UNITLIST)
+ if (yunits <= 0)
+ yunits = RG_UNATIVE
+ }
+ transpose = clgetb ("transpose")
+
+ # Get the output coordinate formatting information.
+ call clgstr ("xformat", Memc[xformat], SZ_FNAME)
+ call clgstr ("yformat", Memc[yformat], SZ_FNAME)
+ call clgstr ("wxformat", Memc[rxformat], SZ_FNAME)
+ call clgstr ("wyformat", Memc[ryformat], SZ_FNAME)
+ min_sigdigits = clgeti ("min_sigdigits")
+
+ # Get remaining parameters.
+ verbose = clgetb ("verbose")
+
+ # Check the formatting of the reference and input logical coordinates.
+ if (Memc[xformat] == EOS) {
+ call sprintf (Memc[xformat], SZ_FNAME, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ }
+ if (Memc[yformat] == EOS) {
+ call sprintf (Memc[yformat], SZ_FNAME, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ }
+
+ # Check the reference image list length.
+ if (imtlen (rlist) <= 0)
+ call error (0, "The reference image list is empty.")
+ if (imtlen(rlist) > 1 && imtlen(rlist) != imtlen(ilist))
+ call error (0,
+ "The number of reference and input images is not the same.")
+
+ # Check the output coordinate file length.
+ if (fntlenb(olist) > 1 && fntlenb(olist) != imtlen(ilist))
+ call error (0,
+ "The number of output coords files and input images is not the same.")
+
+ # Check the reference coordinate list length.
+ if (clist != NULL) {
+ if (fntlenb (clist) != imtlen (rlist))
+ call error (0,
+ "The number of reference coords files and images are not the same")
+ }
+
+ # Initialize the reference image and coordinate list pointers.
+ imr = NULL
+ cfd = NULL
+
+ # Loop over the input images.
+ while (imtgetim (ilist, Memc[image], SZ_FNAME) != EOF) {
+
+ # Open the output file.
+ if (fntgfnb (olist, Memc[str], SZ_FNAME) != EOF) {
+ ofd = open (Memc[str], NEW_FILE, TEXT_FILE)
+ if (ofd == STDOUT)
+ call fseti (ofd, F_FLUSHNL, YES)
+ else if (fntlenb (olist) != imtlen (ilist))
+ call error (0,
+ "The number of output coords files and input images is not the same.")
+ }
+
+ # Open the reference image and reference coordinate file and
+ # compute the logical and world reference coordinates.
+ if (imtgetim (rlist, Memc[refimage], SZ_FNAME) != EOF) {
+
+ # Open the reference image.
+ if (imr != NULL) {
+ call mfree (rxl, TY_DOUBLE)
+ call mfree (ryl, TY_DOUBLE)
+ call mfree (rxw, TY_DOUBLE)
+ call mfree (ryw, TY_DOUBLE)
+ call mfree (ixl, TY_DOUBLE)
+ call mfree (iyl, TY_DOUBLE)
+ if (mwr != NULL)
+ call mw_close (mwr)
+ call imunmap (imr)
+ }
+ imr = immap (Memc[refimage], READ_ONLY, 0)
+ if (IM_NDIM(imr) > 2)
+ call error (0, "The reference image must be 1D or 2D")
+
+ # Open the reference image wcs.
+ iferr (mwr = mw_openim (imr))
+ mwr = NULL
+
+ # Check that the wcs dimensions are rational.
+ if (mwr != NULL) {
+ if (mw_stati(mwr, MW_NPHYSDIM) < IM_NDIM(imr) ||
+ mw_stati (mwr, MW_NDIM) != IM_NDIM(imr)) {
+ call mw_close (mwr)
+ mwr = NULL
+ }
+ }
+
+ # Get the reference image physical and logical axis maps.
+ if (mwr != NULL) {
+ call mw_gaxmap (mwr, Memi[paxno], Memi[rlaxno],
+ mw_stati(mwr, MW_NPHYSDIM))
+ call rg_laxmap (Memi[paxno], mw_stati(mwr, MW_NPHYSDIM),
+ Memi[rlaxno], mw_stati(mwr, MW_NDIM))
+ } else {
+ Memi[rlaxno] = 1
+ Memi[rlaxno+1] = 2
+ }
+
+ # Compute the x limits of the logical reference coordinates.
+ if (IS_INDEFD(xmin))
+ x1 = 1.0d0
+ else
+ x1 = max (1.0d0, min (xmin, double(IM_LEN(imr,1))))
+ if (IS_INDEFD(xmax))
+ x2 = double(IM_LEN(imr,1))
+ else
+ x2 = max (1.0d0, min (xmax, double(IM_LEN(imr,1))))
+
+ # Compute the y limits of the logical reference coordinates.
+ if (IM_NDIM(imr) == 1)
+ y1 = 1.0d0
+ else if (IS_INDEFD(ymin))
+ y1 = 1.0d0
+ else
+ y1 = max (1.0d0, min (ymin, double(IM_LEN(imr,2))))
+ if (IM_NDIM(imr) == 1)
+ y2 = 1.0d0
+ else if (IS_INDEFD(ymax))
+ y2 = double(IM_LEN(imr,2))
+ else
+ y2 = max (1.0d0, min (ymax, double(IM_LEN(imr,2))))
+
+ # Compute the reference logical and world coordinates.
+ if (clist != NULL) {
+
+ if (cfd != NULL)
+ call close (cfd)
+
+ if (fntgfnb (clist, Memc[str], SZ_FNAME) != EOF) {
+ cfd = open (Memc[str], READ_ONLY, TEXT_FILE)
+ npts = rg_rdxy (cfd, rxw, ryw, wcs, xcolumn, ycolumn,
+ xunits, yunits)
+ call malloc (rxl, npts, TY_DOUBLE)
+ call malloc (ryl, npts, TY_DOUBLE)
+ call malloc (ixl, npts, TY_DOUBLE)
+ call malloc (iyl, npts, TY_DOUBLE)
+ if (wcs == RG_WORLD)
+ ctr = rg_xytoxy (mwr, Memd[rxw], Memd[ryw],
+ Memd[rxl], Memd[ryl], npts, "world",
+ "logical", 1, 2)
+ else
+ ctr = rg_xytoxy (mwr, Memd[rxw], Memd[ryw],
+ Memd[rxl], Memd[ryl], npts, "physical",
+ "logical", 1, 2)
+ }
+
+ } else {
+
+ if (IM_NDIM(imr) == 1)
+ npts = nx
+ else
+ npts = nx * ny
+ call malloc (rxl, npts, TY_DOUBLE)
+ call malloc (ryl, npts, TY_DOUBLE)
+ call malloc (rxw, npts, TY_DOUBLE)
+ call malloc (ryw, npts, TY_DOUBLE)
+ call malloc (ixl, npts, TY_DOUBLE)
+ call malloc (iyl, npts, TY_DOUBLE)
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, x1, x2,
+ y1, y2)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, x1, x2,
+ y1, y2)
+ if (wcs == RG_WORLD)
+ ctr = rg_xytoxy (mwr, Memd[rxl], Memd[ryl], Memd[rxw],
+ Memd[ryw], npts, "logical", "world", 1, 2)
+ else
+ ctr = rg_xytoxy (mwr, Memd[rxl], Memd[ryl], Memd[rxw],
+ Memd[ryw], npts, "logical", "physical", 1, 2)
+
+ }
+ }
+
+ # Open the input image.
+ im = immap (Memc[image], READ_ONLY, 0)
+ if (IM_NDIM(im) > 2)
+ call error (0, "The input image must be 1D or 2D")
+ if (IM_NDIM(im) != IM_NDIM(imr))
+ call error (0,
+ "The input image must have same dimensionality as reference image")
+
+ # Open the input wcs.
+ iferr (mw = mw_openim (im))
+ mw = NULL
+ if (mw != NULL) {
+ if (mw_stati(mw, MW_NPHYSDIM) < IM_NDIM(im) ||
+ mw_stati (mw, MW_NDIM) != IM_NDIM(im)) {
+ call mw_close (mw)
+ mw = NULL
+ }
+ }
+
+ # Get the input image wcs physical and logical axis maps.
+ if (mw != NULL) {
+ call mw_gaxmap (mw, Memi[paxno], Memi[laxno], mw_stati(mw,
+ MW_NPHYSDIM))
+ call rg_laxmap (Memi[paxno], mw_stati(mw, MW_NPHYSDIM),
+ Memi[laxno], mw_stati(mw, MW_NDIM))
+ } else {
+ Memi[laxno] = 1
+ Memi[laxno+1] = 2
+ }
+
+ # Write the banner string.
+ call fprintf (ofd,
+ "\n# Reference image: %s Input image: %s\n# \tCoords: %s")
+ call pargstr (Memc[refimage])
+ call pargstr (Memc[image])
+ if (clist == NULL) {
+ call pargstr ("grid")
+ call fprintf (ofd, "\n")
+ } else {
+ call fstats (cfd, F_FILENAME, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call fprintf (ofd, " Wcs: %s\n")
+ switch (wcs) {
+ case RG_PHYSICAL:
+ call pargstr ("physical")
+ case RG_WORLD:
+ call pargstr ("world")
+ default:
+ call pargstr ("world")
+ }
+ }
+
+ # Printe message on the terminal.
+ if (verbose && ofd != STDOUT) {
+ call printf (
+ "\nReference image: %s Input image: %s\n\tCoords: %s")
+ call pargstr (Memc[refimage])
+ call pargstr (Memc[image])
+ if (clist == NULL) {
+ call pargstr ("grid")
+ call printf ("\n")
+ } else {
+ call fstats (cfd, F_FILENAME, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call printf (" Wcs: %s\n")
+ switch (wcs) {
+ case RG_PHYSICAL:
+ call pargstr ("physical")
+ case RG_WORLD:
+ call pargstr ("world")
+ default:
+ call pargstr ("world")
+ }
+ }
+ }
+
+ # Set the reference coordinate formats.
+ if (Memc[rxformat] == EOS)
+ call rg_wsetfmt (mwr, mw, wcs, Memi[rlaxno], Memi[laxno],
+ min_sigdigits, Memc[wxformat], SZ_FNAME)
+ else
+ call strcpy (Memc[rxformat], Memc[wxformat], SZ_FNAME)
+
+ if (Memc[ryformat] == EOS)
+ call rg_wsetfmt (mwr, mw, wcs, Memi[rlaxno+1], Memi[laxno+1],
+ min_sigdigits, Memc[wyformat], SZ_FNAME)
+ else
+ call strcpy (Memc[ryformat], Memc[wyformat], SZ_FNAME)
+
+ # Compute the output coordinates issuing a warning if the
+ # axes types are not compatable.
+ if (mwr == NULL) {
+ call fprintf (ofd,
+ "# \tWarning: reference image wcs is undefined\n")
+ if (verbose && ofd != STDOUT)
+ call printf (
+ "\tWarning: reference image wcs is undefined\n")
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ if (clist == NULL) {
+ call amovd (Memd[rxl], Memd[rxw], npts)
+ call amovd (Memd[ryl], Memd[ryw], npts)
+ }
+ ct = NULL
+ } else if (ctr == NULL) {
+ call fprintf (ofd, "# \tWarning: Unable to compute reference \
+logical <-> world transform\n")
+ if (verbose && ofd != STDOUT) {
+ call printf ("\tWarning: Unable to compute reference \
+logical <-> world transform\n")
+ }
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ if (clist == NULL) {
+ call amovd (Memd[rxl], Memd[rxw], npts)
+ call amovd (Memd[ryl], Memd[ryw], npts)
+ }
+ ct = NULL
+ } else if (mw == NULL) {
+ call fprintf (ofd,
+ "# \tWarning: input image wcs is undefined\n")
+ if (verbose && ofd != STDOUT)
+ call printf ("\tWarning: input image wcs is undefined\n")
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ ct = NULL
+ } else {
+ # Check axis status.
+ if (wcs == RG_PHYSICAL) {
+ axstat = RG_AXEQUAL
+ projstat = RG_AXEQUAL
+ ct = rg_xytoxy (mw, Memd[rxw], Memd[ryw], Memd[ixl],
+ Memd[iyl], npts, "physical", "logical", 1, 2)
+ if (ct == NULL) {
+ call fprintf (ofd,
+ "# \tWarning: Unable to compute image physical -> \
+logical transform\n")
+ if (verbose && ofd != STDOUT) {
+ call printf (
+ "\tWarning: Unable to compute image physical \
+-> logical transform\n")
+ }
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0,
+ double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ }
+ } else {
+ axstat = rg_axstat (mwr, Memi[rlaxno], Memi[rlaxno+1],
+ mw, Memi[laxno], Memi[laxno+1], transpose)
+ projstat = rg_projstat (mwr, Memi[rlaxno], Memi[rlaxno+1],
+ mw, Memi[laxno], Memi[laxno+1])
+ switch (axstat) {
+ case RG_AXEQUAL, RG_AXNOTEQUAL:
+ ct = rg_xytoxy (mw, Memd[rxw], Memd[ryw], Memd[ixl],
+ Memd[iyl], npts, "world", "logical", 1, 2)
+ case RG_AXSWITCHED:
+ ct = rg_xytoxy (mw, Memd[ryw], Memd[rxw], Memd[ixl],
+ Memd[iyl], npts, "world", "logical", 1, 2)
+ }
+ if (ct == NULL) {
+ call fprintf (ofd,
+ "# \tWarning: Unable to compute image \
+ world -> logical transform\n")
+ if (verbose && ofd != STDOUT) {
+ call printf (
+ "\tWarning: Unable to compute image world -> \
+logical transform\n")
+ }
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0,
+ double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ } else if (axstat == RG_AXNOTEQUAL) {
+ call fprintf (ofd,
+ "# \tWarning: Reference and image axtype \
+attributes are different\n")
+ if (verbose && ofd != STDOUT) {
+ call printf (
+ "\tWarning: Reference and image axtype \
+attributes are different\n")
+ }
+ if (IM_NDIM(imr) == 1)
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, 1, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0, 1.0d0)
+ else
+ call rg_rxyl (Memd[rxl], Memd[ryl], nx, ny, 1.0d0,
+ double(IM_LEN(im,1)), 1.0d0,
+ double(IM_LEN(im,2)))
+ call amovd (Memd[rxl], Memd[ixl], npts)
+ call amovd (Memd[ryl], Memd[iyl], npts)
+ } else if (projstat == RG_AXNOTEQUAL) {
+ call fprintf (ofd,
+ "# \tWarning: Reference and image wtype \
+attributes are different\n")
+ if (verbose && ofd != STDOUT) {
+ call printf (
+ "\tWarning: Reference and image wtype \
+attributes are different\n")
+ }
+ }
+ }
+ }
+
+ # Write out the results.
+ call rg_wcoords (ofd, Memd[rxl], Memd[ryl], Memd[ixl],
+ Memd[iyl], Memd[rxw], Memd[ryw], npts, Memc[xformat],
+ Memc[yformat], Memc[wxformat], Memc[wyformat])
+
+ # Close the input image and its wcs.
+ if (mw != NULL)
+ call mw_close (mw)
+ call imunmap (im)
+
+ # Close the output coordinate file if it is not going to
+ # be appended to.
+ if (fntlenb(olist) == imtlen(ilist))
+ call close (ofd)
+ }
+
+ if (imr != NULL) {
+ call mfree (rxl, TY_DOUBLE)
+ call mfree (ryl, TY_DOUBLE)
+ call mfree (rxw, TY_DOUBLE)
+ call mfree (ryw, TY_DOUBLE)
+ call mfree (ixl, TY_DOUBLE)
+ call mfree (iyl, TY_DOUBLE)
+ if (mwr != NULL)
+ call mw_close (mwr)
+ call imunmap (imr)
+ }
+ if (cfd != NULL)
+ call close (cfd)
+ if (fntlenb(olist) < imtlen(ilist))
+ call close (ofd)
+ if (ilist != NULL)
+ call imtclose (ilist)
+ if (rlist != NULL)
+ call imtclose (rlist)
+ if (olist != NULL)
+ call fntclsb (olist)
+ if (clist != NULL)
+ call fntclsb (clist)
+
+ call sfree (sp)
+end
+
+
+# RG_WSETFMT -- Set the world coordinate format.
+
+procedure rg_wsetfmt (mwr, mw, wcs, rlaxno, laxno, min_sigdigits,
+ wformat, maxch)
+
+pointer mwr #I pointer to the reference image wcs
+pointer mw #I pointer to the input image wcs
+int wcs #I the input wcs type
+int rlaxno #I the reference physical axis number
+int laxno #I the input physical axis number
+int min_sigdigits #I the minimum number of significant digits
+char wformat[ARB] #O the output world coordinate format
+int maxch #I the maximum size of the format string
+
+pointer sp, str
+bool streq()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ if (mwr == NULL || mw == NULL) {
+ call sprintf (wformat, maxch, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+
+ } else if (wcs == RG_PHYSICAL) {
+ call strcpy ("%10.3f", wformat, maxch)
+
+ } else {
+ iferr {
+ call mw_gwattrs (mwr, rlaxno, "format", wformat, maxch)
+ } then {
+ iferr {
+ call mw_gwattrs (mw, laxno, "format", wformat, maxch)
+ } then {
+ iferr {
+ call mw_gwattrs (mwr, rlaxno, "axtype", Memc[str],
+ SZ_FNAME)
+ } then {
+ call sprintf (wformat, maxch, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ } else {
+ if (streq (Memc[str], "ra"))
+ call strcpy ("%11.1H", wformat, maxch)
+ else if (streq (Memc[str], "dec"))
+ call strcpy ("%11.1h", wformat, maxch)
+ else if (streq (Memc[str+1], "lon"))
+ call strcpy ("%11.1h", wformat, maxch)
+ else if (streq (Memc[str+1], "lat"))
+ call strcpy ("%11.1h", wformat, maxch)
+ else {
+ call sprintf (wformat, maxch, "%%%d.%dg")
+ call pargi (min_sigdigits + 3)
+ call pargi (min_sigdigits)
+ }
+ }
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_AXSTAT -- Determine whether or not the two axes are equal.
+
+int procedure rg_axstat (mw1, ax11, ax12, mw2, ax21, ax22, transpose)
+
+pointer mw1 #I pointer to the first wcs
+int ax11, ax12 #I the logical reference axes
+pointer mw2 #I pointer to the second wcs
+int ax21, ax22 #I the logical input axes
+bool transpose #I transpose the world coordinates
+
+int stat
+pointer sp, xax1, yax1, xax2, yax2
+bool streq()
+errchk mw_gwattrs()
+
+begin
+ call smark (sp)
+ call salloc (xax1, SZ_FNAME, TY_CHAR)
+ call salloc (yax1, SZ_FNAME, TY_CHAR)
+ call salloc (xax2, SZ_FNAME, TY_CHAR)
+ call salloc (yax2, SZ_FNAME, TY_CHAR)
+
+ iferr (call mw_gwattrs (mw1, ax11, "axtype", Memc[xax1], SZ_FNAME))
+ Memc[xax1] = EOS
+ iferr (call mw_gwattrs (mw1, ax12, "axtype", Memc[yax1], SZ_FNAME))
+ Memc[yax1] = EOS
+ iferr (call mw_gwattrs (mw2, ax21, "axtype", Memc[xax2], SZ_FNAME))
+ Memc[xax2] = EOS
+ iferr (call mw_gwattrs (mw2, ax22, "axtype", Memc[yax2], SZ_FNAME))
+ Memc[yax2] = EOS
+
+ if (transpose)
+ stat = RG_AXSWITCHED
+ else if (streq (Memc[xax1], Memc[xax2]) && streq(Memc[yax1],
+ Memc[yax2]))
+ stat = RG_AXEQUAL
+ else if (streq (Memc[xax1], Memc[yax2]) && streq(Memc[yax1],
+ Memc[xax2]))
+ stat = RG_AXSWITCHED
+ else
+ stat = RG_AXNOTEQUAL
+
+ call sfree (sp)
+
+ return (stat)
+end
+
+
+# RG_PROJSTAT -- Determine whether or not the projections of two axes are equal.
+
+int procedure rg_projstat (mw1, ax11, ax12, mw2, ax21, ax22)
+
+pointer mw1 #I pointer to the first wcs
+int ax11, ax12 #I the logical reference axes
+pointer mw2 #I pointer to the second wcs
+int ax21, ax22 #I the logical reference axes
+
+int stat
+pointer sp, xproj1, yproj1, xproj2, yproj2
+bool streq()
+errchk mw_gwattrs()
+
+begin
+ call smark (sp)
+ call salloc (xproj1, SZ_FNAME, TY_CHAR)
+ call salloc (yproj1, SZ_FNAME, TY_CHAR)
+ call salloc (xproj2, SZ_FNAME, TY_CHAR)
+ call salloc (yproj2, SZ_FNAME, TY_CHAR)
+
+ iferr (call mw_gwattrs (mw1, ax11, "wtype", Memc[xproj1], SZ_FNAME))
+ Memc[xproj1] = EOS
+ iferr (call mw_gwattrs (mw1, ax12, "wtype", Memc[yproj1], SZ_FNAME))
+ Memc[yproj1] = EOS
+ iferr (call mw_gwattrs (mw2, ax21, "wtype", Memc[xproj2], SZ_FNAME))
+ Memc[xproj2] = EOS
+ iferr (call mw_gwattrs (mw2, ax22, "wtype", Memc[yproj2], SZ_FNAME))
+ Memc[yproj2] = EOS
+
+ if (streq (Memc[xproj1], Memc[xproj2]) && streq(Memc[yproj1],
+ Memc[yproj2]))
+ stat = RG_AXEQUAL
+ else if (streq (Memc[xproj1], Memc[yproj2]) && streq(Memc[yproj1],
+ Memc[xproj2]))
+ stat = RG_AXSWITCHED
+ else
+ stat = RG_AXNOTEQUAL
+
+ call sfree (sp)
+
+ return (stat)
+end
+
+
+# RG_WCOORDS -- Write out the reference and input logical coordinates of the
+# tie points and the reference world coordinates.
+
+procedure rg_wcoords (ofd, xref, yref, xin, yin, wxref, wyref, npts,
+ xformat, yformat, wxformat, wyformat)
+
+int ofd #I the output file descriptor
+double xref[ARB] #I the reference logical x coordinates
+double yref[ARB] #I the reference logical y coordinates
+double xin[ARB] #I the input logical x coordinates
+double yin[ARB] #I the input logical y coordinates
+double wxref[ARB] #I the input reference world x coordinates
+double wyref[ARB] #I the input reference world y coordinates
+int npts #I the number of input points
+char xformat[ARB] #I the logical x coordinates format
+char yformat[ARB] #I the logical y coordinates format
+char wxformat[ARB] #I the world x coordinates format
+char wyformat[ARB] #I the world y coordinates format
+
+int i
+pointer sp, fmtstr
+
+begin
+ call smark (sp)
+ call salloc (fmtstr, SZ_LINE, TY_CHAR)
+
+ # Write the column descriptions.
+ call fprintf (ofd,
+ "# \tColumn 1: reference logical x coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 2: reference logical y coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 3: input logical x coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 4: input logical y coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 5: reference world x coordinate\n")
+ call fprintf (ofd,
+ "# \tColumn 6: reference world y coordinate\n")
+ call fprintf (ofd, "\n")
+
+ # Create the format string.
+ call sprintf (Memc[fmtstr], SZ_LINE, "%s %s %s %s %s %s\n")
+ call pargstr (xformat)
+ call pargstr (yformat)
+ call pargstr (xformat)
+ call pargstr (yformat)
+ call pargstr (wxformat)
+ call pargstr (wyformat)
+
+ do i = 1, npts {
+ call fprintf (ofd, Memc[fmtstr])
+ call pargd (xref[i])
+ call pargd (yref[i])
+ call pargd (xin[i])
+ call pargd (yin[i])
+ call pargd (wxref[i])
+ call pargd (wyref[i])
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_LAXMAP (paxno, wcsndim, laxno, ndim)
+
+procedure rg_laxmap (paxno, wcsndim, laxno, ndim)
+
+int paxno[ARB] #I the physical axis map
+int wcsndim #I the number of physical axis dimensions
+int laxno[ARB] #O the physical axis map
+int ndim #I the number of logical axis dimensions
+
+int i, j
+
+begin
+ if (ndim < wcsndim) {
+ do i = 1, ndim {
+ laxno[i] = 0
+ do j = 1, wcsndim {
+ if (paxno[j] != i)
+ next
+ laxno[i] = j
+ break
+ }
+ }
+ do i = ndim + 1, wcsndim
+ laxno[i] = 0
+ } else {
+ do i = 1, wcsndim
+ laxno[i] = i
+ }
+end
diff --git a/pkg/images/immatch/src/wcsmatch/wcsxymatch.h b/pkg/images/immatch/src/wcsmatch/wcsxymatch.h
new file mode 100644
index 00000000..b92673a6
--- /dev/null
+++ b/pkg/images/immatch/src/wcsmatch/wcsxymatch.h
@@ -0,0 +1,15 @@
+# Define the permitted input wcs types
+define RG_WCSLIST "|physical|world|"
+
+define RG_PHYSICAL 1
+define RG_WORLD 2
+
+# Define the permitted units
+define RG_UNITLIST "|hours|native|"
+define RG_UHOURS 1
+define RG_UNATIVE 2
+
+# Define the relationship between the two axes
+define RG_AXEQUAL 1
+define RG_AXSWITCHED 2
+define RG_AXNOTEQUAL 3
diff --git a/pkg/images/immatch/src/xregister/mkpkg b/pkg/images/immatch/src/xregister/mkpkg
new file mode 100644
index 00000000..262b721d
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/mkpkg
@@ -0,0 +1,25 @@
+# Make the XREGISTER task
+
+$checkout libpkg.a ../../../
+$update libpkg.a
+$checkin libpkg.a ../../../
+$exit
+
+libpkg.a:
+ rgxbckgrd.x "xregister.h" <math/gsurfit.h>
+ rgxcolon.x "xregister.h" <imhdr.h> <imset.h> <error.h>
+ rgxcorr.x "xregister.h" <imhdr.h> <math/gsurfit.h> <math.h>
+ rgxdbio.x "xregister.h"
+ rgxfft.x
+ rgxfit.x "xregister.h" <math/iminterp.h> <mach.h> <math/nlfit.h>
+ rgxgpars.x "xregister.h"
+ rgxicorr.x "xregister.h" <ctype.h> <imhdr.h> <fset.h>
+ rgximshift.x <imhdr.h> <imset.h> <math/iminterp.h>
+ rgxplot.x <imhdr.h> <gset.h>
+ rgxppars.x "xregister.h"
+ rgxregions.x "xregister.h" <fset.h> <imhdr.h> <ctype.h>
+ rgxshow.x "xregister.h"
+ rgxtools.x "xregister.h"
+ rgxtransform.x "xregister.h" <imhdr.h> <math.h>
+ t_xregister.x "xregister.h" <fset.h> <gset.h> <imhdr.h> <imset.h>
+ ;
diff --git a/pkg/images/immatch/src/xregister/oxregister.key b/pkg/images/immatch/src/xregister/oxregister.key
new file mode 100644
index 00000000..91064ff8
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/oxregister.key
@@ -0,0 +1,33 @@
+ Xregister Image Overlay Sub-menu
+
+
+? Print help
+c Overlay the marked column of the reference image
+ with the same column of the input image
+l Overlay the marked line of the reference image
+ with the sname line of the input image
+x Overlay the marked column of the reference image
+ with the x and y lagged column of the input image
+y Overlay the marked line of the reference image
+ with the x and y lagged line of the input image
+v Overlay the marked column of the reference image
+ with the x and y shifted column of the input image
+h Overlay the marked line of the reference image
+ with the x and y shifted line of the input image
+q Quit
+
+
+ Image Overlay Sub-menu Colon Commands
+
+:c [m] [n] Overlay the middle [mth] column of the reference image
+ with the mth [nth] column of the input image
+:l [m] [n] Overlay the middle [mth] line of the reference image
+ with the mth [nth] line of the input image
+:x [m] Overlay the middle [mth] column of the reference image
+ with the x and y lagged column of the input image
+:y [m] Overlay the middle [mth] line of the reference image
+ with the x and y lagged line of the input image
+:v [m] Overlay the middle [mth] column of the reference image
+ with the x and y shifted column of the input image
+:h [m] Overlay the middle [mth] line of the reference image
+ with the x and y shifted line of the input image
diff --git a/pkg/images/immatch/src/xregister/rgxbckgrd.x b/pkg/images/immatch/src/xregister/rgxbckgrd.x
new file mode 100644
index 00000000..c9747ee6
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxbckgrd.x
@@ -0,0 +1,63 @@
+include <math/gsurfit.h>
+include "xregister.h"
+
+# RG_XSCALE -- Compute the background offset and x and y slope.
+
+procedure rg_xscale (xc, data, npts, nx, ny, offset, coeff)
+
+pointer xc #I pointer to the cross-correlation function
+real data[ARB] #I the input data
+int npts #I the number of points
+int nx, ny #I the dimensions of the original subraster
+real offset #I the input offset
+real coeff[ARB] #O the output coefficients
+
+int wborder
+pointer gs
+real loreject, hireject, zero
+int rg_xstati(), rg_znsum(), rg_znmedian(), rg_slope()
+real rg_xstatr()
+
+begin
+ loreject = rg_xstatr (xc, LOREJECT)
+ hireject = rg_xstatr (xc, HIREJECT)
+ wborder = rg_xstati (xc, BORDER)
+
+ switch (rg_xstati (xc, BACKGRD)) {
+ case XC_BNONE:
+ coeff[1] = offset
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ case XC_MEAN:
+ if (rg_znsum (data, npts, zero, loreject, hireject) <= 0)
+ zero = 0.0
+ coeff[1] = zero
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ case XC_MEDIAN:
+ if (rg_znmedian (data, npts, zero, loreject, hireject) <= 0)
+ zero = 0.0
+ coeff[1] = zero
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ case XC_SLOPE:
+ call gsinit (gs, GS_POLYNOMIAL, 2, 2, GS_XNONE, 1.0, real (nx), 1.0,
+ real (ny))
+ if (rg_slope (gs, data, npts, nx, ny, wborder, wborder, loreject,
+ hireject) == ERR) {
+ coeff[1] = 0.0
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ } else {
+ call gssave (gs, coeff)
+ coeff[1] = coeff[GS_SAVECOEFF+1]
+ coeff[2] = coeff[GS_SAVECOEFF+2]
+ coeff[3] = coeff[GS_SAVECOEFF+3]
+ }
+ call gsfree (gs)
+ default:
+ coeff[1] = offset
+ coeff[2] = 0.0
+ coeff[3] = 0.0
+ }
+end
diff --git a/pkg/images/immatch/src/xregister/rgxcolon.x b/pkg/images/immatch/src/xregister/rgxcolon.x
new file mode 100644
index 00000000..cb007473
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxcolon.x
@@ -0,0 +1,508 @@
+include <error.h>
+include <imhdr.h>
+include <imset.h>
+include "xregister.h"
+
+# RG_XCOLON-- Procedure to process colon commands for setting the cross-
+# correlation parameters.
+
+procedure rg_xcolon (gd, xc, imr, im1, im2, db, dformat, tfd, reglist, cmdstr,
+ newdata, newcross, newcenter)
+
+pointer gd #I pointer to the graphics stream
+pointer xc #I pointer to cross-correlation structure
+pointer imr #I/O pointer to the reference image
+pointer im1 #I/O pointer to the input image
+pointer im2 #I/O pointer to the output image
+pointer db #I/O pointer to the shifts database file
+int dformat #I is the shifts file in database format
+int tfd #I/O the transformations file descriptor
+pointer reglist #I/O pointer to the regions list
+char cmdstr[ARB] #I input command string
+int newdata #I/O new input data
+int newcross #I/O new cross-correlation function flag
+int newcenter #I/O new cross-correlation peak flag
+
+bool streq()
+int ncmd, creg, nreg, ival, stat
+pointer sp, cmd, str
+real rval
+int strdic(), open(), nscan(), rg_xstati(), fntopnb()
+int rg_xregions(), rg_xmkregions(), strlen()
+pointer immap(), dtmap(), rg_xstatp()
+real rg_xstatr()
+errchk immap(), dtmap(), open(), fntopnb()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get the command.
+ call sscan (cmdstr)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call sfree (sp)
+ return
+ }
+
+ # Process the command.
+ ncmd = strdic (Memc[cmd], Memc[cmd], SZ_LINE, XCMDS)
+ switch (ncmd) {
+ case XCMD_REFIMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (imr != NULL) {
+ call imunmap (imr)
+ imr = NULL
+ }
+ iferr {
+ imr = immap (Memc[cmd], READ_ONLY, 0)
+ } then {
+ call erract (EA_WARN)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ } else if (IM_NDIM(imr) > 2 || IM_NDIM(imr) != IM_NDIM(im1)) {
+ call printf (
+ "Image has the wrong number of dimensions\n")
+ call imunmap (imr)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ } else {
+ call rg_xsets (xc, REFIMAGE, Memc[cmd])
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+ }
+
+ case XCMD_IMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (im1 != NULL) {
+ call imunmap (im1)
+ im1 = NULL
+ }
+ iferr {
+ im1 = immap (Memc[cmd], READ_ONLY, 0)
+ call imseti (im1, IM_TYBNDRY, BT_NEAREST)
+ if (IM_NDIM(im1) == 1)
+ call imseti (im1, IM_NBNDRYPIX, IM_LEN(im1,1))
+ else
+ call imseti (im1, IM_NBNDRYPIX,
+ max (IM_LEN(im1,1), IM_LEN(im1,2)))
+ } then {
+ call erract (EA_WARN)
+ im1 = immap (Memc[str], READ_ONLY, 0)
+ call imseti (im1, IM_TYBNDRY, BT_NEAREST)
+ if (IM_NDIM(im1) == 1)
+ call imseti (im1, IM_NBNDRYPIX, IM_LEN(im1,1))
+ else
+ call imseti (im1, IM_NBNDRYPIX,
+ max (IM_LEN(im1,1), IM_LEN(im1,2)))
+ } else if (IM_NDIM(im1) > 2 || IM_NDIM(im1) != IM_NDIM(imr)) {
+ call printf (
+ "Image has the wrong number of dimensions\n")
+ call imunmap (im1)
+ im1 = immap (Memc[str], READ_ONLY, 0)
+ call imseti (im1, IM_TYBNDRY, BT_NEAREST)
+ if (IM_NDIM(im1) == 1)
+ call imseti (im1, IM_NBNDRYPIX, IM_LEN(im1,1))
+ else
+ call imseti (im1, IM_NBNDRYPIX,
+ max (IM_LEN(im1,1), IM_LEN(im1,2)))
+ } else {
+ call rg_xsets (xc, IMAGE, Memc[cmd])
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+ }
+
+ case XCMD_OUTIMAGE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_xstats (xc, OUTIMAGE, Memc[str], SZ_FNAME)
+ if (im2 == NULL || Memc[cmd] == EOS || streq (Memc[cmd],
+ Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_OUTIMAGE)
+ call pargstr (Memc[str])
+ } else {
+ if (im2 != NULL) {
+ call imunmap (im2)
+ im2 = NULL
+ }
+ iferr {
+ im2 = immap (Memc[cmd], NEW_COPY, im1)
+ } then {
+ call erract (EA_WARN)
+ im2 = immap (Memc[str], NEW_COPY, im1)
+ } else {
+ call rg_xsets (xc, OUTIMAGE, Memc[cmd])
+ }
+ }
+
+ case XCMD_DATABASE:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_xstats (xc, DATABASE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_DATABASE)
+ call pargstr (Memc[str])
+ } else {
+ if (db != NULL) {
+ if (dformat == YES)
+ call dtunmap (db)
+ else
+ call close (db)
+ db = NULL
+ }
+ iferr {
+ if (dformat == YES)
+ db = dtmap (Memc[cmd], APPEND)
+ else
+ db = open (Memc[cmd], NEW_FILE, TEXT_FILE)
+ } then {
+ call erract (EA_WARN)
+ if (dformat == YES)
+ db = dtmap (Memc[str], APPEND)
+ else
+ db = open (Memc[str], APPEND, TEXT_FILE)
+ } else {
+ call rg_xsets (xc, DATABASE, Memc[cmd])
+ }
+ }
+
+ CASE XCMD_RECORD:
+ call gargstr (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_xstats (xc, RECORD, Memc[str], SZ_FNAME)
+ call printf ("%s: %s\n")
+ call pargstr (KY_RECORD)
+ call pargstr (Memc[str])
+ } else
+ call rg_xsets (xc, RECORD, Memc[cmd])
+
+ case XCMD_CREGION:
+
+ call gargi (nreg)
+ creg = rg_xstati (xc, CREGION)
+
+ if (nscan() == 1 || (nreg == creg)) {
+ call printf ("%s: %d/%d")
+ call pargstr (KY_CREGION)
+ call pargi (creg)
+ call pargi (rg_xstati (xc, NREGIONS))
+ call printf (" [%d:%d,%d:%d]\n")
+ call pargi (Memi[rg_xstatp (xc,RC1)+creg-1])
+ call pargi (Memi[rg_xstatp (xc,RC2)+creg-1])
+ call pargi (Memi[rg_xstatp (xc,RL1)+creg-1])
+ call pargi (Memi[rg_xstatp (xc,RL2)+creg-1])
+
+ } else {
+ if (nreg < 1 || nreg > rg_xstati (xc,NREGIONS)) {
+ call printf ("Region %d is out of range\n")
+ call pargi (nreg)
+ } else {
+ call printf (
+ "Setting current region to %d: [%d:%d,%d:%d]\n")
+ call pargi (nreg)
+ call pargi (Memi[rg_xstatp (xc,RC1)+nreg-1])
+ call pargi (Memi[rg_xstatp (xc,RC2)+nreg-1])
+ call pargi (Memi[rg_xstatp (xc,RL1)+nreg-1])
+ call pargi (Memi[rg_xstatp (xc,RL2)+nreg-1])
+ call rg_xseti (xc, CREGION, nreg)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ }
+
+ case XCMD_REGIONS:
+
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_xstats (xc, REGIONS, Memc[str], SZ_FNAME)
+ if (nscan() == 1 || streq (Memc[cmd], Memc[str]) || Memc[cmd] ==
+ EOS) {
+ call printf ("%s [string/file]: %s\n")
+ call pargstr (KY_REGIONS)
+ call pargstr (Memc[str])
+ } else {
+ if (reglist != NULL) {
+ call fntclsb (reglist)
+ reglist = NULL
+ }
+ iferr (reglist = fntopnb (Memc[cmd], NO))
+ reglist = NULL
+ if (rg_xregions (reglist, imr, xc, 1) > 0) {
+ call rg_xseti (xc, CREGION, 1)
+ call rg_xsets (xc, REGIONS, Memc[cmd])
+ newdata = YES; newcross = YES; newcenter = YES
+ } else {
+ if (reglist != NULL) {
+ call fntclsb (reglist)
+ reglist = NULL
+ }
+ iferr (reglist = fntopnb (Memc[str], NO))
+ reglist = NULL
+ if (rg_xregions (reglist, imr, xc, 1) > 0)
+ ;
+ call rg_xsets (xc, REGIONS, Memc[str])
+ call rg_xseti (xc, CREGION, 1)
+ }
+ }
+
+ case XCMD_REFFILE:
+
+ call gargwrd (Memc[cmd], SZ_LINE)
+ call rg_xstats (xc, REFFILE, Memc[str], SZ_FNAME)
+ if (Memc[cmd] == EOS || streq (Memc[cmd], Memc[str])) {
+ call printf ("%s: %s\n")
+ call pargstr (KY_REFFILE)
+ call pargstr (Memc[str])
+ } else {
+ if (tfd != NULL) {
+ call close (tfd)
+ tfd = NULL
+ }
+ iferr {
+ tfd = open (Memc[cmd], READ_ONLY, TEXT_FILE)
+ } then {
+ tfd = NULL
+ call erract (EA_WARN)
+ call rg_xsets (xc, REFFILE, "")
+ call printf ("Coords file is undefined.\n")
+ } else
+ call rg_xsets (xc, REFFILE, Memc[cmd])
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_XLAG:
+ call gargi (ival)
+ if (nscan () == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_XLAG)
+ call pargi (rg_xstati (xc, XLAG))
+ } else {
+ call rg_xseti (xc, XLAG, ival)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_YLAG:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_YLAG)
+ call pargi (rg_xstati (xc, YLAG))
+ } else {
+ call rg_xseti (xc, YLAG, ival)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_DXLAG:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_DXLAG)
+ call pargi (rg_xstati (xc, DXLAG))
+ } else {
+ call rg_xseti (xc, DXLAG, ival)
+ }
+
+ case XCMD_DYLAG:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_DYLAG)
+ call pargi (rg_xstati (xc, DYLAG))
+ } else {
+ call rg_xseti (xc, DYLAG, ival)
+ }
+
+ case XCMD_BACKGROUND:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] != EOS)
+ call strcat (" ", Memc[cmd], SZ_LINE)
+ call gargwrd (Memc[cmd+strlen(Memc[cmd])], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_xstats (xc, BSTRING, Memc[str], SZ_FNAME)
+ call printf ("%s: %s\n")
+ call pargstr (KY_BACKGROUND)
+ call pargstr (Memc[str])
+ } else {
+ call rg_xsets (xc, BSTRING, Memc[cmd])
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_BORDER:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_BORDER)
+ call pargi (rg_xstati (xc, BORDER))
+ } else {
+ call rg_xseti (xc, BORDER, ival)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_LOREJECT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_LOREJECT)
+ call pargr (rg_xstatr (xc, LOREJECT))
+ } else {
+ call rg_xsetr (xc, LOREJECT, rval)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_HIREJECT:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_HIREJECT)
+ call pargr (rg_xstatr (xc, HIREJECT))
+ } else {
+ call rg_xsetr (xc, HIREJECT, rval)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_APODIZE:
+ call gargr (rval)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_APODIZE)
+ call pargr (rg_xstatr (xc, APODIZE))
+ } else {
+ call rg_xsetr (xc, APODIZE, max (0.0, min (rval, 0.50)))
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_CORRELATION:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_xstats (xc, CSTRING, Memc[str], SZ_FNAME)
+ call printf ("%s = %s\n")
+ call pargstr (KY_CORRELATION)
+ call pargstr (Memc[str])
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, XC_CTYPES)
+ if (stat > 0) {
+ call rg_xseti (xc, CFUNC, stat)
+ call rg_xsets (xc, CSTRING, Memc[cmd])
+ newcross = YES; newcenter = YES
+ }
+ }
+
+ case XCMD_XWINDOW:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_XWINDOW)
+ call pargi (rg_xstati (xc, XWINDOW))
+ } else {
+ call rg_xseti (xc, XWINDOW, ival)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_YWINDOW:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_YWINDOW)
+ call pargi (rg_xstati (xc, YWINDOW))
+ } else {
+ call rg_xseti (xc, YWINDOW, ival)
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+
+ case XCMD_PEAKCENTER:
+ call gargwrd (Memc[cmd], SZ_LINE)
+ if (Memc[cmd] == EOS) {
+ call rg_xstats (xc, PSTRING, Memc[str], SZ_FNAME)
+ call printf ("%s: %s\n")
+ call pargstr (KY_PEAKCENTER)
+ call pargstr (Memc[str])
+ } else {
+ stat = strdic (Memc[cmd], Memc[cmd], SZ_LINE, XC_PTYPES)
+ if (stat > 0) {
+ call rg_xseti (xc, PFUNC, stat)
+ call rg_xsets (xc, PSTRING, Memc[cmd])
+ newcenter = YES
+ }
+ }
+
+ case XCMD_XCBOX:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %d\n")
+ call pargstr (KY_XCBOX)
+ call pargi (rg_xstati (xc, XCBOX))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_xseti (xc, XCBOX, ival)
+ newcenter = YES
+ }
+
+ case XCMD_YCBOX:
+ call gargi (ival)
+ if (nscan() == 1) {
+ call printf ("%s = %g\n")
+ call pargstr (KY_YCBOX)
+ call pargi (rg_xstati (xc, YCBOX))
+ } else {
+ if (mod (ival, 2) == 0)
+ ival = ival + 1
+ call rg_xseti (xc, YCBOX, ival)
+ newcenter = YES
+ }
+
+ case XCMD_SHOW:
+ call gdeactivate (gd, 0)
+ call gargwrd (Memc[cmd], SZ_LINE)
+ ncmd = strdic (Memc[cmd], Memc[cmd], SZ_LINE, XSHOW)
+ switch (ncmd) {
+ case XSHOW_DATA:
+ call rg_xnshow (xc)
+ case XSHOW_BACKGROUND:
+ call rg_xbshow (xc)
+ case XSHOW_CORRELATION:
+ call rg_xxshow (xc)
+ case XSHOW_PEAKCENTER:
+ call rg_xpshow (xc)
+ default:
+ call rg_xshow (xc)
+ }
+ call greactivate (gd, 0)
+
+ case XCMD_MARK:
+ call gdeactivate (gd, 0)
+ if (reglist != NULL) {
+ call fntclsb (reglist)
+ reglist = NULL
+ }
+ if (rg_xmkregions (imr, xc, 1, MAX_NREGIONS, Memc[str],
+ SZ_LINE) <= 0) {
+ call rg_xstats (xc, REGIONS, Memc[str], SZ_LINE)
+ iferr (reglist = fntopnb (Memc[str], NO))
+ reglist = NULL
+ if (rg_xregions (reglist, imr, xc, 1) > 0)
+ ;
+ call rg_xsets (xc, REGIONS, Memc[str])
+ call rg_xseti (xc, CREGION, 1)
+ } else {
+ call rg_xseti (xc, CREGION, 1)
+ call rg_xsets (xc, REGIONS, Memc[str])
+ newdata = YES; newcross = YES; newcenter = YES
+ }
+ call greactivate (gd, 0)
+ default:
+ call printf ("Unknown or ambiguous colon command\7\n")
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/xregister/rgxcorr.x b/pkg/images/immatch/src/xregister/rgxcorr.x
new file mode 100644
index 00000000..a708bf7a
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxcorr.x
@@ -0,0 +1,1034 @@
+include <imhdr.h>
+include <math.h>
+include <math/gsurfit.h>
+include "xregister.h"
+
+# RG_XCORR -- Compute the shift shift for an image relative to a reference
+# image using cross-correlation techniques.
+
+int procedure rg_xcorr (imr, im1, db, dformat, xc)
+
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer db #I pointer to the shifts database
+int dformat #I write shifts file in database format ?
+pointer xc #I pointer to the cross-correlation structure
+
+pointer sp, image, imname
+real xshift, yshift
+bool streq()
+int rg_xstati(), fscan(), nscan()
+errchk rg_cross(), rg_xfile()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+ call salloc (imname, SZ_FNAME, TY_CHAR)
+ call rg_xstats (xc, IMAGE, Memc[image], SZ_FNAME)
+
+ # Initialize.
+ xshift = 0.0
+ yshift = 0.0
+
+ # Compute the average shift for the image.
+ switch (rg_xstati (xc, CFUNC)) {
+ case XC_DISCRETE, XC_DIFFERENCE, XC_FOURIER:
+
+ # Write out the parameters.
+ if (dformat == YES)
+ call rg_xdbparams (db, xc)
+
+ # Compute the cross-correlation function.
+ call rg_cross (imr, im1, xc, NULL, xshift, yshift)
+ call rg_xsetr (xc, TXSHIFT, xshift)
+ call rg_xsetr (xc, TYSHIFT, yshift)
+
+ # Write out the results for the individual regions.
+ if (dformat == YES)
+ call rg_xwreg (db, xc)
+
+ # Write out the total shifts.
+ if (dformat == YES)
+ call rg_xdbshift (db, xc)
+ else {
+ call fprintf (db, "%s %g %g\n")
+ call pargstr (Memc[image])
+ call pargr (xshift)
+ call pargr (yshift)
+ }
+
+ # Set the x and y lags for the next picture.
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ call rg_xseti (xc, XLAG, 0)
+ call rg_xseti (xc, YLAG, 0)
+ } else if (IS_INDEFI (rg_xstati (xc, DXLAG)) ||
+ IS_INDEFI (rg_xstati (xc, DYLAG))) {
+ call rg_xseti (xc, XLAG, nint (-xshift))
+ call rg_xseti (xc, YLAG, nint (-yshift))
+ } else {
+ call rg_xseti (xc, XLAG, rg_xstati (xc, XLAG) + rg_xstati (xc,
+ DXLAG))
+ call rg_xseti (xc, YLAG, rg_xstati (xc, YLAG) + rg_xstati (xc,
+ DYLAG))
+ }
+
+ case XC_FILE:
+ if (dformat == YES)
+ call rg_xfile (db, xc, xshift, yshift)
+ else {
+ if (fscan (db) != EOF) {
+ call gargwrd (Memc[imname], SZ_FNAME)
+ call gargr (xshift)
+ call gargr (yshift)
+ if (! streq (Memc[imname], Memc[image]) || nscan() != 3) {
+ xshift = 0.0
+ yshift = 0.0
+ }
+ } else {
+ xshift = 0.0
+ yshift = 0.0
+ }
+ }
+ call rg_xsetr (xc, TXSHIFT, xshift)
+ call rg_xsetr (xc, TYSHIFT, yshift)
+
+ default:
+ call error (0, "The correlation function is undefined.")
+ }
+
+ call sfree (sp)
+
+ return (NO)
+end
+
+
+# RG_CROSS -- Compute the cross-correlation function for all the regions
+# using discrete, fourier, or difference techniques and compute the position
+# of its peak using one of several centering algorithms.
+
+procedure rg_cross (imr, im1, xc, gd, xavshift, yavshift)
+
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+pointer xc #I pointer to the cross correlation structure
+pointer gd #I pointer to graphics stream
+real xavshift #O x coord shift
+real yavshift #O y coord shift
+
+int i, nregions, ngood
+pointer pxshift, pyshift
+real xshift, yshift
+int rg_xstati(), rg_xcget(), rg_xfget()
+pointer rg_xstatp()
+
+begin
+ # Get the pointers.
+ pxshift = rg_xstatp (xc, XSHIFTS)
+ pyshift = rg_xstatp (xc, YSHIFTS)
+ nregions = rg_xstati (xc, NREGIONS)
+
+ # Loop over the regions.
+ xavshift = 0.0
+ yavshift = 0.0
+ ngood = 0
+ do i = 1, nregions {
+
+ # Compute the cross_correlation function.
+ switch (rg_xstati (xc, CFUNC)) {
+ case XC_DISCRETE, XC_DIFFERENCE:
+ if (rg_xcget (xc, imr, im1, i) == ERR) {
+ Memr[pxshift+i-1] = INDEFR
+ Memr[pyshift+i-1] = INDEFR
+ if (rg_xstatp (xc, XCOR) != NULL)
+ call mfree (rg_xstatp (xc, XCOR), TY_REAL)
+ call rg_xsetp (xc, XCOR, NULL)
+ next
+ }
+ case XC_FOURIER:
+ if (rg_xfget (xc, imr, im1, i) == ERR) {
+ Memr[pxshift+i-1] = INDEFR
+ Memr[pyshift+i-1] = INDEFR
+ if (rg_xstatp (xc, XCOR) != NULL)
+ call mfree (rg_xstatp (xc, XCOR), TY_REAL)
+ call rg_xsetp (xc, XCOR, NULL)
+ next
+ }
+ default:
+ call error (0, "The correlation function is undefined")
+ }
+
+ # Find the peak of the cross-correlation function.
+ call rg_fit (xc, i, gd, xshift, yshift)
+
+ # Accumulate the shifts.
+ xavshift = xavshift + xshift
+ yavshift = yavshift + yshift
+ ngood = ngood + 1
+ }
+
+ # Compute the average shift.
+ if (ngood > 0) {
+ xavshift = xavshift / ngood
+ yavshift = yavshift / ngood
+ }
+end
+
+
+# RG_XFILE -- Read the average x and y shifts from the shifts database.
+
+procedure rg_xfile (db, xc, xshift, yshift)
+
+pointer db #I pointer to the database
+pointer xc #I pointer to the cross correlation structure
+real xshift #O shift in x
+real yshift #O shift in y
+
+int rec
+pointer sp, str
+int dtlocate()
+real dtgetr()
+errchk dtlocate(), dtgetr()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ call rg_xstats (xc, RECORD, Memc[str], SZ_LINE)
+ iferr {
+ rec = dtlocate (db, Memc[str])
+ xshift = dtgetr (db, rec, "xshift")
+ yshift = dtgetr (db, rec, "yshift")
+ } then {
+ xshift = 0.0
+ yshift = 0.0
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_ICROSS -- Compute the cross-correlation function for a given region.
+
+int procedure rg_icross (xc, imr, im1, nreg)
+
+pointer xc #I pointer to the cross-correlation structure
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+int nreg #I the index of the current region
+
+int stat
+pointer pxshift, pyshift
+int rg_xstati(), rg_xcget(), rg_xfget()
+pointer rg_xstatp()
+
+begin
+ pxshift = rg_xstatp (xc, XSHIFTS)
+ pyshift = rg_xstatp (xc, YSHIFTS)
+
+ switch (rg_xstati (xc, CFUNC)) {
+ case XC_DISCRETE, XC_DIFFERENCE:
+ stat = rg_xcget (xc, imr, im1, nreg)
+ if (stat == ERR) {
+ Memr[pxshift+nreg-1] = INDEFR
+ Memr[pyshift+nreg-1] = INDEFR
+ if (rg_xstatp (xc, XCOR) != NULL)
+ call mfree (rg_xstatp (xc, XCOR), TY_REAL)
+ call rg_xsetp (xc, XCOR, NULL)
+ }
+ case XC_FOURIER:
+ stat = rg_xfget (xc, imr, im1, nreg)
+ if (stat == ERR) {
+ Memr[pxshift+nreg-1] = INDEFR
+ Memr[pyshift+nreg-1] = INDEFR
+ if (rg_xstatp (xc, XCOR) != NULL)
+ call mfree (rg_xstatp (xc, XCOR), TY_REAL)
+ call rg_xsetp (xc, XCOR, NULL)
+ }
+ case XC_FILE:
+ stat = OK
+ }
+
+ return (stat)
+end
+
+
+# RG_XCGET -- Compute the convolution using the discrete or difference
+# correlation functions.
+
+int procedure rg_xcget (xc, imr, im1, i)
+
+pointer xc #I pointer to the cross-correlation structure
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to input image image
+int i #I index of region
+
+int stat, xwindow, ywindow, nrimcols, nrimlines, nimcols, nimlines
+int nrcols, nrlines, ncols, nlines
+int xlag, ylag, nborder, rc1, rc2, rl1, rl2, c1, c2, l1, l2
+pointer sp, str, coeff, rbuf, ibuf, xcor
+pointer prc1, prc2, prl1, prl2, przero, prxslope, pryslope, border
+real rxlag, rylag
+int rg_xstati(), rg_border()
+pointer rg_xstatp(), rg_ximget()
+real rg_xstatr()
+
+define nextregion_ 10
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (coeff, max (GS_SAVECOEFF + 6, 9), TY_REAL)
+ rbuf = NULL
+ ibuf = NULL
+
+ # Check for regions.
+ if (i > rg_xstati (xc, NREGIONS)) {
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Get the image sizes.
+ nrimcols = IM_LEN(imr,1)
+ if (IM_NDIM(imr) == 1)
+ nrimlines = 1
+ else
+ nrimlines = IM_LEN(imr,2)
+ nimcols = IM_LEN(im1,1)
+ if (IM_NDIM(im1) == 1)
+ nimlines = 1
+ else
+ nimlines = IM_LEN(im1,2)
+
+ # Get the reference region pointers.
+ prc1 = rg_xstatp (xc, RC1)
+ prc2 = rg_xstatp (xc, RC2)
+ prl1 = rg_xstatp (xc, RL1)
+ prl2 = rg_xstatp (xc, RL2)
+ przero = rg_xstatp (xc, RZERO)
+ prxslope = rg_xstatp (xc, RXSLOPE)
+ pryslope = rg_xstatp (xc, RYSLOPE)
+
+ # Compute the reference region limits.
+ rc1 = max (1, min (int (nrimcols), Memi[prc1+i-1]))
+ rc2 = min (int (nrimcols), max (1, Memi[prc2+i-1]))
+ rl1 = max (1, min (int (nrimlines), Memi[prl1+i-1]))
+ rl2 = min (int (nrimlines), max (1, Memi[prl2+i-1]))
+ nrcols = rc2 - rc1 + 1
+ nrlines = rl2 - rl1 + 1
+
+ # Move to the next reference region if current region is off the image.
+ if (rc1 > nrimcols || rc2 < 1 || rl1 > nrimlines || rl2 < 1) {
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Reference section: %s[%d:%d,%d:%d] is off image.\n")
+ call pargstr (Memc[str])
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (rl1)
+ call pargi (rl2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Check the window sizes.
+ xwindow = rg_xstati (xc, XWINDOW)
+ if (nrlines == 1)
+ ywindow = 1
+ else
+ ywindow = rg_xstati (xc, YWINDOW)
+
+ # Move to next ref regions if current region is too small.
+ if (nrcols < xwindow || (IM_NDIM(imr) == 2 && nrlines < ywindow)) {
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Reference section: %s[%d:%d,%d:%d] has too few points.\n")
+ call pargstr (Memc[str])
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (rl1)
+ call pargi (rl2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Apply the transformation if defined or lag to the ref regions.
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ call rg_etransform (xc, (rc1 + rc2) / 2.0, (rl1 + rl2) / 2.0,
+ rxlag, rylag)
+ xlag = rxlag - (rc1 + rc2) / 2.0
+ if (ywindow == 1)
+ ylag = 0
+ else
+ ylag = rylag - (rl1 + rl2) / 2.0
+ } else {
+ xlag = rg_xstati (xc, XLAG)
+ if (ywindow == 1)
+ ylag = 0
+ else
+ ylag = rg_xstati (xc, YLAG)
+ }
+
+ # Get the input image limits.
+ c1 = rc1 + xlag - xwindow / 2
+ c2 = rc2 + xlag + xwindow / 2
+ l1 = rl1 + ylag - ywindow / 2
+ l2 = rl2 + ylag + ywindow / 2
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+
+ # Move to the next ref region if input region is off image.
+ if (c1 > nimcols || c2 < 1 || l1 > nimlines || l2 < 1) {
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Image section: %s[%d:%d,%d:%d] is off image.\n")
+ call pargstr (Memc[str])
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (l1)
+ call pargi (l2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Move to the next ref region if input region is less than 3 by 3.
+ if ((ncols < xwindow) || (IM_NDIM(im1) == 2 && nlines < ywindow)) {
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Image section: %s[%d:%d,%d:%d] has too few points.\n")
+ call pargstr (Memc[str])
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (l1)
+ call pargi (l2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Get the input reference and input image data.
+ rbuf = rg_ximget (imr, rc1, rc2, rl1, rl2)
+ if (rbuf == NULL) {
+ stat = ERR
+ goto nextregion_
+ }
+ ibuf = rg_ximget (im1, c1, c2, l1, l2)
+ if (ibuf == NULL) {
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Do the background subtraction.
+
+ # Compute the zero point, x slope and y slope of ref image.
+ if (IS_INDEFR(Memr[przero+i-1]) || IS_INDEFR(Memr[prxslope+i- 1]) ||
+ IS_INDEFR(Memr[pryslope+i-1])) {
+ if (IS_INDEFI(rg_xstati (xc, BORDER))) {
+ call rg_xscale (xc, Memr[rbuf], nrcols * nrlines, nrcols,
+ nrlines, rg_xstatr (xc, BVALUER), Memr[coeff])
+ } else {
+ border = NULL
+ nborder = rg_border (Memr[rbuf], nrcols, nrlines,
+ max (0, nrcols - 2 * rg_xstati (xc, BORDER)),
+ max (0, nrlines - 2 * rg_xstati (xc, BORDER)),
+ border)
+ call rg_xscale (xc, Memr[border], nborder, nrcols,
+ nrlines, rg_xstatr (xc, BVALUER), Memr[coeff])
+ if (border != NULL)
+ call mfree (border, TY_REAL)
+ }
+
+ # Save the coefficients.
+ Memr[przero+i-1] = Memr[coeff]
+ Memr[prxslope+i-1] = Memr[coeff+1]
+ Memr[pryslope+i-1] = Memr[coeff+2]
+ }
+
+ call rg_subtract (Memr[rbuf], nrcols, nrlines, Memr[przero+i-1],
+ Memr[prxslope+i-1], Memr[pryslope+i-1])
+
+ # Compute the zero point, and the x and y slopes of input image.
+ if (IS_INDEFI(rg_xstati (xc, BORDER))) {
+ call rg_xscale (xc, Memr[ibuf], ncols * nlines, ncols,
+ nlines, rg_xstatr (xc, BVALUE), Memr[coeff])
+ } else {
+ border = NULL
+ nborder = rg_border (Memr[ibuf], ncols, nlines,
+ max (0, ncols - 2 * rg_xstati (xc, BORDER)),
+ max (0, nlines - 2 * rg_xstati (xc, BORDER)),
+ border)
+ call rg_xscale (xc, Memr[border], nborder, ncols, nlines,
+ rg_xstatr (xc, BVALUE), Memr[coeff])
+ if (border != NULL)
+ call mfree (border, TY_REAL)
+ }
+
+ # Subtract the baseline.
+ call rg_subtract (Memr[ibuf], ncols, nlines, Memr[coeff],
+ Memr[coeff+1], Memr[coeff+2])
+
+ # Apodize the data.
+ if (rg_xstatr (xc, APODIZE) > 0.0) {
+ call rg_apodize (Memr[rbuf], nrcols, nrlines, rg_xstatr (xc,
+ APODIZE), YES)
+ call rg_apodize (Memr[ibuf], ncols, nlines, rg_xstatr (xc,
+ APODIZE), YES)
+ }
+
+ # Spatially filter the data with a Laplacian.
+ switch (rg_xstati (xc, FILTER)) {
+ case XC_LAPLACE:
+ call rg_xlaplace (Memr[rbuf], nrcols, nrlines, 1.0)
+ call rg_xlaplace (Memr[ibuf], ncols, nlines, 1.0)
+ default:
+ ;
+ }
+
+ # Allocate space for the cross-correlation function.
+ if (rg_xstatp (xc, XCOR) == NULL) {
+ call malloc (xcor, xwindow * ywindow, TY_REAL)
+ call rg_xsetp (xc, XCOR, xcor)
+ } else {
+ xcor = rg_xstatp (xc, XCOR)
+ call realloc (xcor, xwindow * ywindow, TY_REAL)
+ call rg_xsetp (xc, XCOR, xcor)
+ }
+
+ # Clear the correlation function.
+ call aclrr (Memr[xcor], xwindow * ywindow)
+
+ # Compute the cross-correlation function.
+ if (rg_xstati (xc, CFUNC) == XC_DISCRETE) {
+ call rg_xconv (Memr[rbuf], nrcols, nrlines, Memr[ibuf], ncols,
+ nlines, Memr[xcor], xwindow, ywindow)
+ } else {
+ call rg_xdiff (Memr[rbuf], nrcols, nrlines, Memr[ibuf], ncols,
+ nlines, Memr[xcor], xwindow, ywindow)
+ }
+
+ stat = OK
+
+nextregion_
+
+ # Free memory.
+ call sfree (sp)
+ if (rbuf != NULL)
+ call mfree (rbuf, TY_REAL)
+ if (ibuf != NULL)
+ call mfree (ibuf, TY_REAL)
+ if (stat == ERR)
+ return (ERR)
+ else
+ return (OK)
+end
+
+
+# RG_XFGET -- Compute the cross-correlation function using Fourier techniques.
+
+int procedure rg_xfget (xc, imr, im1, i)
+
+pointer xc #I pointer to the cross-correlation structure
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+int i #I index of the current region
+
+int rc1, rc2, rl1, rl2, nrcols, nrlines, c1, c2, l1, l2, ncols, nlines
+int nrimcols, nrimlines, nimcols, nimlines
+int xwindow, ywindow, xlag, nxfft, nyfft, ylag, stat, nborder
+pointer sp, str, coeff, xcor, rbuf, ibuf, fft, border
+pointer prc1, prc2, prl1, prl2, przero, prxslope, pryslope
+real rxlag, rylag
+int rg_xstati(), rg_border(), rg_szfft()
+pointer rg_xstatp(), rg_ximget()
+real rg_xstatr()
+
+define nextregion_ 11
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (coeff, max (GS_SAVECOEFF+6, 9), TY_REAL)
+
+ # Check for number of regions.
+ if (i > rg_xstati (xc, NREGIONS)) {
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Allocate space for the cross-correlation function.
+ nrimcols = IM_LEN(imr,1)
+ if (IM_NDIM(imr) == 1)
+ nrimlines = 1
+ else
+ nrimlines = IM_LEN(imr,2)
+ nimcols = IM_LEN(im1,1)
+ if (IM_NDIM(im1) == 1)
+ nimlines = 1
+ else
+ nimlines = IM_LEN(im1,2)
+
+ # Get the regions pointers.
+ prc1 = rg_xstatp (xc, RC1)
+ prc2 = rg_xstatp (xc, RC2)
+ prl1 = rg_xstatp (xc, RL1)
+ prl2 = rg_xstatp (xc, RL2)
+ przero = rg_xstatp (xc, RZERO)
+ prxslope = rg_xstatp (xc, RXSLOPE)
+ pryslope = rg_xstatp (xc, RYSLOPE)
+
+ # Get the reference subraster region.
+ rc1 = max (1, min (int (nrimcols), Memi[prc1+i-1]))
+ rc2 = min (int (nrimcols), max (1, Memi[prc2+i-1]))
+ rl1 = max (1, min (int (nrimlines), Memi[prl1+i-1]))
+ rl2 = min (int (nrimlines), max (1, Memi[prl2+i-1]))
+ nrcols = rc2 - rc1 + 1
+ nrlines = rl2 - rl1 + 1
+
+ # Go to next region if the reference region is off the image.
+ if (rc1 > nrimcols || rc2 < 1 || rl1 > nrimlines || rl2 < 1) {
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Reference section: %s[%d:%d,%d:%d] is off image.\n")
+ call pargstr (Memc[str])
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (rl1)
+ call pargi (rl2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Check the window sizes.
+ xwindow = rg_xstati (xc, XWINDOW)
+ if (nrlines == 1)
+ ywindow = 1
+ else
+ ywindow = rg_xstati (xc, YWINDOW)
+
+ # Go to the next region if the reference region has too few points.
+ if ((nrcols < xwindow) || (IM_NDIM(im1) == 2 && nrlines < ywindow)) {
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Reference section: %s[%d:%d,%d:%d] has too few points.\n")
+ call pargstr (Memc[str])
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (rl1)
+ call pargi (rl2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Apply the transformation if defined or the lag.
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ call rg_etransform (xc, (rc1 + rc2) / 2.0, (rl1 + rl2) / 2.0,
+ rxlag, rylag)
+ xlag = rxlag - (rc1 + rc2) / 2.0
+ if (ywindow == 1)
+ ylag = 0
+ else
+ ylag = rylag - (rl1 + rl2) / 2.0
+ } else {
+ xlag = rg_xstati (xc, XLAG)
+ if (ywindow == 1)
+ ylag = 0
+ else
+ ylag = rg_xstati (xc, YLAG)
+ }
+
+ # Get the input image subraster regions.
+ c1 = rc1 + xlag
+ c2 = rc2 + xlag
+ l1 = rl1 + ylag
+ l2 = rl2 + ylag
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+
+ # Go to next region if region is off the image.
+ if (c1 > nimcols || c2 < 1 || l1 > nimlines || l2 < 1) {
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Image section: %s[%d:%d,%d:%d] is off image.\n")
+ call pargstr (Memc[str])
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (l1)
+ call pargi (l2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Go to next region if region has too few points.
+ if ((ncols < xwindow) || (IM_NDIM(im1) == 2 && nlines < ywindow)) {
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_LINE)
+ call eprintf (
+ "Image section: %s[%d:%d,%d:%d] has too few points.\n")
+ call pargstr (Memc[str])
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (l1)
+ call pargi (l2)
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Figure out how big the Fourier transform has to be, given
+ # the size of the reference subraster, the window size and
+ # the fact that the FFT must be a power of 2.
+
+ nxfft = rg_szfft (nrcols, xwindow)
+ if (ywindow == 1)
+ nyfft = 1
+ else
+ nyfft = rg_szfft (nrlines, ywindow)
+ call calloc (fft, 2 * nxfft * nyfft, TY_REAL)
+
+ # Get the input reference and input image data.
+ rbuf = NULL
+ rbuf = rg_ximget (imr, rc1, rc2, rl1, rl2)
+ if (rbuf == NULL) {
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Do the background subtraction.
+
+ # Compute the zero point, x slope and y slope of ref image.
+ if (IS_INDEFR(Memr[przero+i-1]) || IS_INDEFR(Memr[prxslope+i- 1]) ||
+ IS_INDEFR(Memr[pryslope+i-1])) {
+ if (IS_INDEFI(rg_xstati (xc, BORDER))) {
+ call rg_xscale (xc, Memr[rbuf], nrcols * nrlines, nrcols,
+ nrlines, rg_xstatr (xc, BVALUER), Memr[coeff])
+ } else {
+ border = NULL
+ nborder = rg_border (Memr[rbuf], nrcols, nrlines,
+ max (0, nrcols - 2 * rg_xstati (xc, BORDER)),
+ max (0, nrlines - 2 * rg_xstati (xc, BORDER)),
+ border)
+ call rg_xscale (xc, Memr[border], nborder, nrcols,
+ nrlines, rg_xstatr (xc, BVALUER), Memr[coeff])
+ if (border != NULL)
+ call mfree (border, TY_REAL)
+ }
+
+ # Save the coefficients.
+ Memr[przero+i-1] = Memr[coeff]
+ Memr[prxslope+i-1] = Memr[coeff+1]
+ Memr[pryslope+i-1] = Memr[coeff+2]
+ }
+
+ call rg_subtract (Memr[rbuf], nrcols, nrlines, Memr[przero+i-1],
+ Memr[prxslope+i-1], Memr[pryslope+i-1])
+
+ # Apodize the data.
+ if (rg_xstatr (xc, APODIZE) > 0.0)
+ call rg_apodize (Memr[rbuf], nrcols, nrlines, rg_xstatr (xc,
+ APODIZE), YES)
+
+ # Spatially filter the data with a Laplacian.
+ switch (rg_xstati (xc, FILTER)) {
+ case XC_LAPLACE:
+ call rg_xlaplace (Memr[rbuf], nrcols, nrlines, 1.0)
+ default:
+ ;
+ }
+
+ # Load the reference data into the FFT.
+ call rg_rload (Memr[rbuf], nrcols, nrlines, Memr[fft], nxfft, nyfft)
+ call mfree (rbuf, TY_REAL)
+
+ ibuf = NULL
+ ibuf = rg_ximget (im1, c1, c2, l1, l2)
+ if (ibuf == NULL) {
+ stat = ERR
+ goto nextregion_
+ }
+
+ # Compute the zero point, and the x and y slopes of input image.
+ if (IS_INDEFI(rg_xstati (xc, BORDER))) {
+ call rg_xscale (xc, Memr[ibuf], ncols * nlines, ncols,
+ nlines, rg_xstatr (xc, BVALUE), Memr[coeff])
+ } else {
+ border = NULL
+ nborder = rg_border (Memr[ibuf], ncols, nlines,
+ max (0, ncols - 2 * rg_xstati (xc, BORDER)),
+ max (0, nlines - 2 * rg_xstati (xc, BORDER)),
+ border)
+ call rg_xscale (xc, Memr[border], nborder, ncols, nlines,
+ rg_xstatr (xc, BVALUE), Memr[coeff])
+ if (border != NULL)
+ call mfree (border, TY_REAL)
+ }
+
+ # Subtract the baseline.
+ call rg_subtract (Memr[ibuf], ncols, nlines, Memr[coeff],
+ Memr[coeff+1], Memr[coeff+2])
+
+ # Apodize the data.
+ if (rg_xstatr (xc, APODIZE) > 0.0)
+ call rg_apodize (Memr[ibuf], ncols, nlines, rg_xstatr (xc,
+ APODIZE), YES)
+
+ # Spatially filter the data with a Laplacian.
+ switch (rg_xstati (xc, FILTER)) {
+ case XC_LAPLACE:
+ call rg_xlaplace (Memr[ibuf], ncols, nlines, 1.0)
+ default:
+ ;
+ }
+
+ # Load the image data into the FFT.
+ call rg_iload (Memr[ibuf], ncols, nlines, Memr[fft], nxfft, nyfft)
+ call mfree (ibuf, TY_REAL)
+
+ # Normalize the data.
+ call rg_fnorm (Memr[fft], nrcols, nrlines, nxfft, nyfft)
+
+ # Compute the cross-correlation function.
+ call rg_fftcor (Memr[fft], nxfft, nyfft)
+
+ # Allocate space for the correlation function.
+ if (rg_xstatp (xc, XCOR) == NULL) {
+ call malloc (xcor, xwindow * ywindow, TY_REAL)
+ call rg_xsetp (xc, XCOR, xcor)
+ } else {
+ xcor = rg_xstatp (xc, XCOR)
+ call realloc (xcor, xwindow * ywindow, TY_REAL)
+ call rg_xsetp (xc, XCOR, xcor)
+ }
+
+ # Move the valid lags into the crosscorrelation array
+ call rg_movexr (Memr[fft], nxfft, nyfft, Memr[xcor], xwindow, ywindow)
+
+ # Free space.
+ call mfree (fft, TY_REAL)
+
+ stat = OK
+
+nextregion_
+
+ call sfree (sp)
+ if (stat == ERR)
+ return (ERR)
+ else
+ return (OK)
+end
+
+
+# RG_XIMGET -- Fill a buffer from a specified region of the image.
+
+pointer procedure rg_ximget (im, c1, c2, l1, l2)
+
+pointer im #I pointer to the iraf image
+int c1, c2 #I column limits in the input image
+int l1, l2 #I line limits in the input image
+
+int i, ncols, nlines, npts
+pointer ptr, index, buf
+pointer imgs1r(), imgs2r()
+
+begin
+ ncols = c2 - c1 + 1
+ nlines = l2 - l1 + 1
+ npts = ncols * nlines
+ call malloc (ptr, npts, TY_REAL)
+
+ index = ptr
+ do i = l1, l2 {
+ if (IM_NDIM(im) == 1)
+ buf = imgs1r (im, c1, c2)
+ else
+ buf = imgs2r (im, c1, c2, i, i)
+ call amovr (Memr[buf], Memr[index], ncols)
+ index = index + ncols
+ }
+
+ return (ptr)
+end
+
+
+# RG_XLAPLACE -- Compute the Laplacian of an image subraster in place.
+
+procedure rg_xlaplace (data, nx, ny, rho)
+
+real data[nx,ARB] #I the input array
+int nx, ny #I the size of the input/output data array
+real rho #I the pixel to pixel correlation factor
+
+int i, inline, outline, nxk, nyk, nxc
+pointer sp, lineptrs, ptr
+real rhosq, kernel[3,3]
+data nxk /3/, nyk /3/
+
+begin
+ # Define the kernel.
+ rhosq = rho * rho
+ kernel[1,1] = rhosq
+ kernel[2,1] = -rho * (1.0 + rhosq)
+ kernel[3,1] = rhosq
+ kernel[1,2] = -rho * (1.0 + rhosq)
+ kernel[2,2] = (1.0 + rhosq) * (1 + rhosq)
+ kernel[3,2] = -rho * (1.0 + rhosq)
+ kernel[1,3] = rhosq
+ kernel[2,3] = -rho * (1.0 + rhosq)
+ kernel[3,3] = rhosq
+
+ # Set up an array of line pointers.
+ call smark (sp)
+ call salloc (lineptrs, nyk, TY_POINTER)
+
+ # Allocate working space.
+ nxc = nx + 2 * (nxk / 2)
+ do i = 1, nyk
+ call salloc (Memi[lineptrs+i-1], nxc, TY_REAL)
+
+ inline = 1 - nyk / 2
+ do i = 1, nyk - 1 {
+ if (inline < 1) {
+ call amovr (data[1,1], Memr[Memi[lineptrs+i]+nxk/2], nx)
+ Memr[Memi[lineptrs+i]] = data[1,1]
+ Memr[Memi[lineptrs+i]+nxc-1] = data[nx,1]
+ } else {
+ call amovr (data[1,i-1], Memr[Memi[lineptrs+i]+nxk/2], nx)
+ Memr[Memi[lineptrs+i]] = data[1,i-1]
+ Memr[Memi[lineptrs+i]+nxc-1] = data[nx,i-1]
+ }
+ inline = inline + 1
+ }
+
+ # Generate the output image line by line
+ do outline = 1, ny {
+
+ # Scroll the input buffers
+ ptr = Memi[lineptrs]
+ do i = 1, nyk - 1
+ Memi[lineptrs+i-1] = Memi[lineptrs+i]
+ Memi[lineptrs+nyk-1] = ptr
+
+ # Read in new image line
+ if (inline > ny) {
+ call amovr (data[1,ny], Memr[Memi[lineptrs+nyk-1]+nxk/2],
+ nx)
+ Memr[Memi[lineptrs+nyk-1]] = data[1,ny]
+ Memr[Memi[lineptrs+nyk-1]+nxc-1] = data[nx,ny]
+ } else {
+ call amovr (data[1,inline], Memr[Memi[lineptrs+nyk-1]+nxk/2],
+ nx)
+ Memr[Memi[lineptrs+nyk-1]] = data[1,inline]
+ Memr[Memi[lineptrs+nyk-1]+nxc-1] = data[nx,inline]
+ }
+
+ # Generate output image line
+ call aclrr (data[1,outline], nx)
+ do i = 1, nyk
+ call acnvr (Memr[Memi[lineptrs+i-1]], data[1,outline], nx,
+ kernel[1,i], nxk)
+
+ inline = inline + 1
+ }
+
+ # Free the image buffer pointers
+ call sfree (sp)
+end
+
+
+# RG_XCONV -- Compute the cross-correlation function directly in the spatial
+# domain.
+
+procedure rg_xconv (ref, nrcols, nrlines, image, ncols, nlines, xcor, xwindow,
+ ywindow)
+
+real ref[nrcols,nrlines] #I the input reference subraster
+int nrcols, nrlines #I size of the reference subraster
+real image[ncols,nlines] #I the input image subraster
+int ncols, nlines #I size of the image subraster
+real xcor[xwindow,ywindow] #O the output cross-correlation function
+int xwindow, ywindow #I size of the cross-correlation function
+
+int lagx, lagy, i, j
+real meanr, facr, meani, faci, sum
+real asumr()
+#real cxmin, cxmax
+
+begin
+ meanr = asumr (ref, nrcols * nrlines) / (nrcols * nrlines)
+ facr = 0.0
+ do j = 1, nrlines {
+ do i = 1, nrcols
+ facr = facr + (ref[i,j] - meanr) ** 2
+ }
+ if (facr <= 0.0)
+ facr = 1.0
+ else
+ facr = sqrt (facr)
+
+ do lagy = 1, ywindow {
+ do lagx = 1, xwindow {
+ meani = 0.0
+ do j = 1, nrlines {
+ do i = 1, nrcols
+ meani = meani + image[i+lagx-1,j+lagy-1]
+ }
+ meani = meani / (nrcols * nrlines)
+ faci = 0.0
+ sum = 0.0
+ do j = 1, nrlines {
+ do i = 1, nrcols {
+ faci = faci + (image[i+lagx-1,j+lagy-1] - meani) ** 2
+ sum = sum + (ref[i,j] - meanr) *
+ (image[i+lagx-1,j+lagy-1] - meani)
+ }
+ }
+ if (faci <= 0.0)
+ faci = 1.0
+ else
+ faci = sqrt (faci)
+ xcor[lagx,lagy] = sum / facr / faci
+ }
+ }
+end
+
+
+# RG_XDIFF -- Compute the error function at each of several templates.
+
+procedure rg_xdiff (ref, nrcols, nrlines, image, ncols, nlines, xcor, xwindow,
+ ywindow)
+
+real ref[nrcols,nrlines] #I reference subraste
+int nrcols, nrlines #I size of the reference subraster
+real image[ncols,nlines] #I image subraster
+int ncols, nlines #I size of image subraster
+real xcor[xwindow,ywindow] #O crosscorrelation function
+int xwindow, ywindow #I size of correlation function
+
+int lagx, lagy, i, j
+real meanr, meani, sum, cormin, cormax
+real asumr()
+
+
+begin
+ meanr = asumr (ref, nrcols * nrlines) / (nrcols * nrlines)
+ do lagy = 1, ywindow {
+ do lagx = 1, xwindow {
+ meani = 0.0
+ do j = 1, nrlines {
+ do i = 1, nrcols
+ meani = meani + image[i+lagx-1,j+lagy-1]
+ }
+ meani = meani / (nrcols * nrlines)
+ sum = 0.0
+ do j = 1, nrlines {
+ do i = 1, nrcols {
+ sum = sum + abs ((ref[i,j] - meanr) -
+ (image[i+lagx-1,j+lagy-1] - meani))
+ }
+ }
+ xcor[lagx,lagy] = sum
+ }
+ }
+
+ call alimr (xcor, xwindow * ywindow, cormin, cormax)
+ call adivkr (xcor, cormax, xcor, xwindow * ywindow)
+ call asubkr (xcor, 1.0, xcor, xwindow * ywindow)
+ call anegr (xcor, xcor, xwindow * ywindow)
+end
+
diff --git a/pkg/images/immatch/src/xregister/rgxdbio.x b/pkg/images/immatch/src/xregister/rgxdbio.x
new file mode 100644
index 00000000..3e197636
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxdbio.x
@@ -0,0 +1,290 @@
+include "xregister.h"
+
+# RG_XWREC -- Procedure to write out the whole record.
+
+procedure rg_xwrec (db, dformat, xc)
+
+pointer db #I pointer to the database file
+int dformat #I is the shifts file in database format
+pointer xc #I pointer to the cross correlation structure
+
+int i, nregions, ngood, c1, c2, l1, l2, xlag, ylag
+pointer sp, image, prc1, prc2, prl1, prl2, pxshift, pyshift
+real xin, yin, xout, yout, xavshift, yavshift
+int rg_xstati()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (image, SZ_FNAME, TY_CHAR)
+
+ # Write the header record.
+ if (dformat == YES)
+ call rg_xdbparams (db, xc)
+
+ # Fetch the pointers to the columns.
+ prc1 = rg_xstatp (xc, RC1)
+ prc2 = rg_xstatp (xc, RC2)
+ prl1 = rg_xstatp (xc, RL1)
+ prl2 = rg_xstatp (xc, RL2)
+ pxshift = rg_xstatp (xc, XSHIFTS)
+ pyshift = rg_xstatp (xc, YSHIFTS)
+ nregions = rg_xstati (xc, NREGIONS)
+
+ xavshift = 0.0
+ yavshift = 0.0
+ ngood = 0
+ do i = 1, nregions {
+
+ xin = (Memi[prc1+i-1] + Memi[prc2+i-1]) / 2.0
+ yin = (Memi[prl1+i-1] + Memi[prl2+i-1]) / 2.0
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ call rg_etransform (xc, xin, yin, xout, yout)
+ xlag = xout - xin
+ ylag = yout - yin
+ } else {
+ xlag = rg_xstati (xc, XLAG)
+ ylag = rg_xstati (xc, YLAG)
+ }
+ c1 = Memi[prc1+i-1] + xlag
+ c2 = Memi[prc2+i-1] + xlag
+ l1 = Memi[prl1+i-1] + ylag
+ l2 = Memi[prl2+i-1] + ylag
+
+ if (IS_INDEFR(Memr[pxshift+i-1]) || IS_INDEFR(Memr[pyshift+i-1])) {
+ if (dformat == YES)
+ call rg_xdbshiftr (db, Memi[prc1+i-1], Memi[prc2+i-1],
+ Memi[prl1+i-1], Memi[prl2+i-1], c1, c2, l1, l2,
+ INDEFR, INDEFR)
+ } else {
+ if (dformat == YES)
+ call rg_xdbshiftr (db, Memi[prc1+i-1], Memi[prc2+i-1],
+ Memi[prl1+i-1], Memi[prl2+i-1], c1, c2, l1, l2,
+ Memr[pxshift+i-1], Memr[pyshift+i-1])
+ ngood = ngood + 1
+ xavshift = xavshift + Memr[pxshift+i-1]
+ yavshift = yavshift + Memr[pyshift+i-1]
+ }
+ }
+
+ # Compute the average shift.
+ if (ngood <= 0) {
+ xavshift = 0.0
+ yavshift = 0.0
+ } else {
+ xavshift = xavshift / ngood
+ yavshift = yavshift / ngood
+ }
+ call rg_xsetr (xc, TXSHIFT, xavshift)
+ call rg_xsetr (xc, TYSHIFT, yavshift)
+
+ if (dformat == YES)
+ call rg_xdbshift (db, xc)
+ else {
+ call rg_xstats (xc, IMAGE, Memc[image], SZ_FNAME)
+ call fprintf (db, "%s %g %g\n")
+ call pargstr (Memc[image])
+ call pargr (xavshift)
+ call pargr (yavshift)
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_XDBPARAMS -- Write the cross-correlation parameters to the database file.
+
+procedure rg_xdbparams (db, xc)
+
+pointer db #I pointer to the database file
+pointer xc #I pointer to the cross-correlation structure
+
+pointer sp, str
+int rg_xstati()
+#real rg_xstatr()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+
+ # Write out the time record was written.
+ call dtput (db, "\n")
+ call dtptime (db)
+
+ # Write out the record name.
+ call rg_xstats (xc, RECORD, Memc[str], SZ_FNAME)
+ call dtput (db, "begin\t%s\n")
+ call pargstr (Memc[str])
+
+ # Write the image names.
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_FNAME)
+ call dtput (db, "\t%s\t\t%s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_FNAME)
+ call dtput (db, "\t%s\t%s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str])
+
+ call dtput (db, "\t%s\t%d\n")
+ call pargstr (KY_NREGIONS)
+ call pargi (rg_xstati (xc, NREGIONS))
+
+ call sfree (sp)
+end
+
+
+# RG_XWREG -- Write out the results for each region individually into
+# the shifts file.
+
+procedure rg_xwreg (db, xc)
+
+pointer db #I pointer to the database file
+pointer xc #I pointer to the cross-correlation structure
+
+int i, nregions, c1, c2, l1, l2, xlag, ylag
+pointer prc1, prc2, prl1, prl2, pxshift, pyshift
+real xin, yin, xout, yout
+int rg_xstati()
+pointer rg_xstatp()
+
+begin
+ # Fetch the regions pointers.
+ prc1 = rg_xstatp (xc, RC1)
+ prc2 = rg_xstatp (xc, RC2)
+ prl1 = rg_xstatp (xc, RL1)
+ prl2 = rg_xstatp (xc, RL2)
+ pxshift = rg_xstatp (xc, XSHIFTS)
+ pyshift = rg_xstatp (xc, YSHIFTS)
+ nregions = rg_xstati (xc, NREGIONS)
+
+ # Write out the reference image region(s) and the equivalent
+ # input image regions.
+ do i = 1, nregions {
+
+ xin = (Memi[prc1+i-1] + Memi[prc2+i-1]) / 2.0
+ yin = (Memi[prl1+i-1] + Memi[prl2+i-1]) / 2.0
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ call rg_etransform (xc, xin, yin, xout, yout)
+ xlag = xout - xin
+ ylag = yout - yin
+ } else {
+ xlag = rg_xstati (xc, XLAG)
+ ylag = rg_xstati (xc, YLAG)
+ }
+ c1 = Memi[prc1+i-1] + xlag
+ c2 = Memi[prc2+i-1] + xlag
+ l1 = Memi[prl1+i-1] + ylag
+ l2 = Memi[prl2+i-1] + ylag
+
+ if (IS_INDEFR(Memr[pxshift+i-1]) || IS_INDEFR(Memr[pyshift+i-1]))
+ call rg_xdbshiftr (db, Memi[prc1+i-1], Memi[prc2+i-1],
+ Memi[prl1+i-1], Memi[prl2+i-1], c1, c2, l1, l2,
+ INDEFR, INDEFR)
+ else
+ call rg_xdbshiftr (db, Memi[prc1+i-1], Memi[prc2+i-1],
+ Memi[prl1+i-1], Memi[prl2+i-1], c1, c2, l1, l2,
+ Memr[pxshift+i-1], Memr[pyshift+i-1])
+ }
+end
+
+
+# RG_XDBSHIFTR -- Write out the reference image section, input image
+# section and x and y shifts for each region.
+
+procedure rg_xdbshiftr (db, rc1, rc2, rl1, rl2, c1, c2, l1, l2, xshift, yshift)
+
+pointer db #I pointer to the database file
+int rc1, rc2 #I reference region column limits
+int rl1, rl2 #I reference region line limits
+int c1, c2 #I image region column limits
+int l1, l2 #I image region line limits
+real xshift #I x shift
+real yshift #I y shift
+
+begin
+ call dtput (db,"\t[%d:%d,%d:%d]\t[%d:%d,%d:%d]\t%g\t%g\n")
+ call pargi (rc1)
+ call pargi (rc2)
+ call pargi (rl1)
+ call pargi (rl2)
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (l1)
+ call pargi (l2)
+ call pargr (xshift)
+ call pargr (yshift)
+end
+
+
+# RG_XDBSHIFT -- Write the average shifts to the shifts database.
+
+procedure rg_xdbshift (db, xc)
+
+pointer db #I pointer to text database file
+pointer xc #I pointer to the cross-correlation structure
+
+real rg_xstatr()
+
+begin
+ call dtput (db, "\t%s\t\t%g\n")
+ call pargstr (KY_TXSHIFT)
+ call pargr (rg_xstatr (xc, TXSHIFT))
+ call dtput (db, "\t%s\t\t%g\n")
+ call pargstr (KY_TYSHIFT)
+ call pargr (rg_xstatr (xc, TYSHIFT))
+end
+
+
+# RG_XPWREC -- Print the computed shift for a region.
+
+procedure rg_xpwrec (xc, i)
+
+pointer xc #I pointer to the cross-correlation structure
+int i #I the current region
+
+int xlag, ylag, c1, c2, l1, l2
+pointer prc1, prc2, prl1, prl2
+real xin, yin, rxlag, rylag
+int rg_xstati()
+pointer rg_xstatp()
+
+begin
+ # Fetch the pointers to the reference regions.
+ prc1 = rg_xstatp (xc, RC1)
+ prc2 = rg_xstatp (xc, RC2)
+ prl1 = rg_xstatp (xc, RL1)
+ prl2 = rg_xstatp (xc, RL2)
+
+ # Transform the reference region to the input region.
+ xin = (Memi[prc1+i-1] + Memi[prc2+i-1]) / 2.0
+ yin = (Memi[prl1+i-1] + Memi[prl2+i-1]) / 2.0
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ call rg_etransform (xc, xin, yin, rxlag, rylag)
+ xlag = rxlag - xin
+ ylag = rylag - yin
+ } else {
+ xlag = rg_xstati (xc, XLAG)
+ ylag = rg_xstati (xc, YLAG)
+ }
+
+ c1 = Memi[prc1+i-1] + xlag
+ c2 = Memi[prc2+i-1] + xlag
+ l1 = Memi[prl1+i-1] + ylag
+ l2 = Memi[prl2+i-1] + ylag
+
+ # Print the results.
+ call printf ("Region %d: [%d:%d,%d:%d] [%d:%d,%d:%d] %g %g\n")
+ call pargi (i)
+ call pargi (Memi[prc1+i-1])
+ call pargi (Memi[prc2+i-1])
+ call pargi (Memi[prl1+i-1])
+ call pargi (Memi[prl2+i-1])
+ call pargi (c1)
+ call pargi (c2)
+ call pargi (l1)
+ call pargi (l2)
+ call pargr (Memr[rg_xstatp(xc,XSHIFTS)+i-1])
+ call pargr (Memr[rg_xstatp(xc,YSHIFTS)+i-1])
+end
diff --git a/pkg/images/immatch/src/xregister/rgxfft.x b/pkg/images/immatch/src/xregister/rgxfft.x
new file mode 100644
index 00000000..8847cf56
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxfft.x
@@ -0,0 +1,179 @@
+# RG_FFTCOR -- Compute the FFT of the reference and image data, take their
+# product, and compute the inverse transform to get the cross-correlation
+# function. The reference and input image are loaded into alternate memory
+# locations.
+
+procedure rg_fftcor (fft, nxfft nyfft)
+
+real fft[ARB] #I/O array to be fft'd
+int nxfft, nyfft #I dimensions of the fft
+
+pointer sp, dim
+
+begin
+ call smark (sp)
+ call salloc (dim, 2, TY_INT)
+
+ # Fourier transform the two arrays.
+ Memi[dim] = nxfft
+ Memi[dim+1] = nyfft
+ if (Memi[dim+1] == 1)
+ call rg_fourn (fft, Memi[dim], 1, 1)
+ else
+ call rg_fourn (fft, Memi[dim], 2, 1)
+
+ # Compute the product of the two transforms.
+ call rg_mulfft (fft, fft, 2 * nxfft, nyfft)
+
+ # Shift the array to center the transform.
+ call rg_fshift (fft, fft, 2 * nxfft, nyfft)
+
+ # Normalize the transform.
+ call adivkr (fft, real (nxfft * nyfft), fft, 2 * nxfft * nyfft)
+
+ # Compute the inverse transform.
+ if (Memi[dim+1] == 1)
+ call rg_fourn (fft, Memi[dim], 1, -1)
+ else
+ call rg_fourn (fft, Memi[dim], 2, -1)
+
+ call sfree (sp)
+end
+
+
+# RG_MULFFT -- Unpack the two individual ffts and compute their product.
+
+procedure rg_mulfft (fft1, fft2, nxfft, nyfft)
+
+real fft1[nxfft,nyfft] #I array containing 2 ffts of 2 real functions
+real fft2[nxfft,nyfft] #O fft of correlation function
+int nxfft, nyfft #I dimensions of fft
+
+int i,j, nxd2p2, nxp2, nxp3, nyd2p1, nyp2
+real c1, c2, h1r, h1i, h2r, h2i
+
+begin
+ c1 = 0.5
+ c2 = -0.5
+
+ nxd2p2 = nxfft / 2 + 2
+ nxp2 = nxfft + 2
+ nxp3 = nxfft + 3
+ nyd2p1 = nyfft / 2 + 1
+ nyp2 = nyfft + 2
+
+ # Compute the 0 frequency point.
+ h1r = fft1[1,1]
+ h1i = 0.0
+ h2r = fft1[2,1]
+ h2i = 0.0
+ fft2[1,1] = h1r * h2r
+ fft2[2,1] = 0.0
+
+ # Compute the x axis points.
+ do i = 3, nxd2p2, 2 {
+ h2r = c1 * (fft1[i,1] + fft1[nxp2-i,1])
+ h2i = c1 * (fft1[i+1,1] - fft1[nxp3-i,1])
+ h1r = -c2 * (fft1[i+1,1] + fft1[nxp3-i,1])
+ h1i = c2 * (fft1[i,1] - fft1[nxp2-i,1])
+ fft2[i,1] = (h1r * h2r + h1i * h2i)
+ fft2[i+1,1] = (h1i * h2r - h2i * h1r)
+ fft2[nxp2-i,1] = fft2[i,1]
+ fft2[nxp3-i,1] = - fft2[i+1,1]
+ }
+
+ # Quit if the transform is 1D.
+ if (nyfft < 2)
+ return
+
+ # Compute the y axis points.
+ do i = 2, nyd2p1 {
+ h2r = c1 * (fft1[1,i] + fft1[1, nyp2-i])
+ h2i = c1 * (fft1[2,i] - fft1[2,nyp2-i])
+ h1r = -c2 * (fft1[2,i] + fft1[2,nyp2-i])
+ h1i = c2 * (fft1[1,i] - fft1[1,nyp2-i])
+ fft2[1,i] = (h1r * h2r + h1i * h2i)
+ fft2[2,i] = (h1i * h2r - h2i * h1r)
+ fft2[1,nyp2-i] = fft2[1,i]
+ fft2[2,nyp2-i] = - fft2[2,i]
+ }
+
+ # Compute along the axis of symmetry.
+ do i = 3, nxd2p2, 2 {
+ h2r = c1 * (fft1[i,nyd2p1] + fft1[nxp2-i, nyd2p1])
+ h2i = c1 * (fft1[i+1,nyd2p1] - fft1[nxp3-i,nyd2p1])
+ h1r = -c2 * (fft1[i+1,nyd2p1] + fft1[nxp3-i,nyd2p1])
+ h1i = c2 * (fft1[i,nyd2p1] - fft1[nxp2-i,nyd2p1])
+ fft2[i,nyd2p1] = (h1r * h2r + h1i * h2i)
+ fft2[i+1,nyd2p1] = (h1i * h2r - h2i * h1r)
+ fft2[nxp2-i,nyd2p1] = fft2[i,nyd2p1]
+ fft2[nxp3-i,nyd2p1] = - fft2[i+1,nyd2p1]
+ }
+
+ # Compute the remainder of the transform.
+ do j = 2, nyd2p1 - 1 {
+ do i = 3, nxfft, 2 {
+ h2r = c1 * (fft1[i,j] + fft1[nxp2-i, nyp2-j])
+ h2i = c1 * (fft1[i+1,j] - fft1[nxp3-i,nyp2-j])
+ h1r = -c2 * (fft1[i+1,j] + fft1[nxp3-i,nyp2-j])
+ h1i = c2 * (fft1[i,j] - fft1[nxp2-i,nyp2-j])
+ fft2[i,j] = (h1r * h2r + h1i * h2i)
+ fft2[i+1,j] = (h1i * h2r - h2i * h1r)
+ fft2[nxp2-i,nyp2-j] = fft2[i,j]
+ fft2[nxp3-i,nyp2-j] = - fft2[i+1,j]
+ }
+ }
+end
+
+
+# RG_FNORM -- Normalize the reference and image data before computing
+# the fft's.
+
+procedure rg_fnorm (array, ncols, nlines, nxfft, nyfft)
+
+real array[ARB] #I/O the input/output data array
+int ncols, nlines #I dimensions of the input data array
+int nxfft, nyfft #I dimensions of the fft
+
+int i, j, index
+real sumr, sumi, meanr, meani
+
+begin
+ # Compute the mean.
+ sumr = 0.0
+ sumi = 0.0
+ index = 0
+ do j = 1, nlines {
+ do i = 1, ncols {
+ sumr = sumr + array[index+2*i-1]
+ sumi = sumi + array[index+2*i]
+ }
+ index = index + 2 * nxfft
+ }
+ meanr = sumr / (ncols * nlines)
+ meani = sumi / (ncols * nlines)
+
+ # Compute the sigma.
+ sumr = 0.0
+ sumi = 0.0
+ index = 0
+ do j = 1, nlines {
+ do i = 1, ncols {
+ sumr = sumr + (array[index+2*i-1] - meanr) ** 2
+ sumi = sumi + (array[index+2*i] - meani) ** 2
+ }
+ index = index + 2 * nxfft
+ }
+ sumr = sqrt (sumr)
+ sumi = sqrt (sumi)
+
+ # Normalize the data.
+ index = 0
+ do j = 1, nlines {
+ do i = 1, ncols {
+ array[index+2*i-1] = (array[index+2*i-1] - meanr) / sumr
+ array[index+2*i] = (array[index+2*i] - meani) / sumi
+ }
+ index = index + 2 * nxfft
+ }
+end
diff --git a/pkg/images/immatch/src/xregister/rgxfit.x b/pkg/images/immatch/src/xregister/rgxfit.x
new file mode 100644
index 00000000..34e6398c
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxfit.x
@@ -0,0 +1,814 @@
+include <mach.h>
+include <math/iminterp.h>
+include <math/nlfit.h>
+include "xregister.h"
+
+define NL_MAXITER 10
+define NL_TOL 0.001
+
+# RG_FIT -- Fit the peak of the cross-correlation function using one of the
+# fitting functions.
+
+procedure rg_fit (xc, nreg, gd, xshift, yshift)
+
+pointer xc #I the pointer to the cross-corrrelation structure
+int nreg #I the current region
+pointer gd #I the pointer to the graphics stream
+real xshift, yshift #O the computed shifts
+
+int nrlines, xwindow, ywindow, xcbox, ycbox, xlag, ylag
+real xin, yin, xout, yout
+int rg_xstati()
+pointer rg_xstatp()
+
+begin
+ # Check the window and centering box sizes.
+ nrlines = Memi[rg_xstatp(xc,RL2)+nreg-1] -
+ Memi[rg_xstatp(xc,RL1)+nreg-1] + 1
+ xwindow = rg_xstati (xc, XWINDOW)
+ if (nrlines == 1)
+ ywindow = 1
+ else
+ ywindow = rg_xstati (xc, YWINDOW)
+ xcbox = rg_xstati (xc, XCBOX)
+ if (nrlines == 1)
+ ycbox = 1
+ else
+ ycbox = rg_xstati (xc, YCBOX)
+
+ # Do the centering.
+ switch (rg_xstati (xc, PFUNC)) {
+ case XC_PNONE:
+ call rg_maxmin (Memr[rg_xstatp(xc,XCOR)], xwindow, ywindow,
+ xshift, yshift)
+ case XC_CENTROID:
+ call rg_imean (Memr[rg_xstatp(xc,XCOR)], xwindow,
+ ywindow, xcbox, ycbox, xshift, yshift)
+ case XC_SAWTOOTH:
+ call rg_sawtooth (Memr[rg_xstatp(xc,XCOR)], xwindow,
+ ywindow, xcbox, ycbox, xshift, yshift)
+ case XC_PARABOLA:
+ call rg_iparabolic (Memr[rg_xstatp(xc,XCOR)], xwindow, ywindow,
+ xcbox, ycbox, xshift, yshift)
+ case XC_MARK:
+ if (gd == NULL)
+ call rg_imean (Memr[rg_xstatp(xc,XCOR)], xwindow,
+ ywindow, xcbox, ycbox, xshift, yshift)
+ else
+ call rg_xmkpeak (gd, xwindow, ywindow, xshift, yshift)
+ default:
+ call rg_imean (Memr[rg_xstatp(xc,XCOR)], xwindow, ywindow,
+ xcbox, ycbox, xshift, yshift)
+ }
+
+ # Store the shifts.
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ xin = (Memi[rg_xstatp(xc,RC1)+nreg-1] +
+ Memi[rg_xstatp(xc,RC2)+nreg-1]) / 2.0
+ yin = (Memi[rg_xstatp(xc,RL1)+nreg-1] +
+ Memi[rg_xstatp(xc,RL2)+nreg-1]) / 2.0
+ call rg_etransform (xc, xin, yin, xout, yout)
+ xlag = xout - xin
+ ylag = yout - yin
+ } else {
+ xlag = rg_xstati (xc, XLAG)
+ ylag = rg_xstati (xc, YLAG)
+ }
+ xshift = - (xshift + xlag)
+ yshift = - (yshift + ylag)
+ Memr[rg_xstatp(xc,XSHIFTS)+nreg-1] = xshift
+ Memr[rg_xstatp(xc,YSHIFTS)+nreg-1] = yshift
+end
+
+
+# RG_MAXMIN -- Procedure to compute the peak of the cross-correlation function
+# by determining the maximum point.
+
+procedure rg_maxmin (xcor, xwindow, ywindow, xshift, yshift)
+
+real xcor[xwindow,ywindow] #I the cross-correlation function
+int xwindow, ywindow #I dimensions of cross-correlation function
+real xshift, yshift #O x and shift of the peak
+
+int xindex, yindex
+
+begin
+ # Locate the maximum point.
+ call rg_alim2r (xcor, xwindow, ywindow, xindex, yindex)
+ xshift = xindex - (1.0 + xwindow) / 2.0
+ yshift = yindex - (1.0 + ywindow) / 2.0
+end
+
+
+# RG_IMEAN -- Compute the peak of the cross-correlation function using the
+# intensity weighted mean of the marginal distributions in x and y.
+
+procedure rg_imean (xcor, xwindow, ywindow, xcbox, ycbox, xshift, yshift)
+
+real xcor[xwindow,ARB] #I the cross-correlation function
+int xwindow, ywindow #I dimensions of the cross-correlation function
+int xcbox, ycbox #I dimensions of the centering box
+real xshift, yshift #O x and y shift of cross-correlation function
+
+int xindex, yindex, xlo, xhi, ylo, yhi, nx, ny
+pointer sp, xmarg, ymarg
+
+begin
+ call smark (sp)
+ call salloc (xmarg, xcbox, TY_REAL)
+ call salloc (ymarg, ycbox, TY_REAL)
+
+ # Locate the maximum point and normalize.
+ call rg_alim2r (xcor, xwindow, ywindow, xindex, yindex)
+
+ # Compute the limits of the centering box.
+ xlo = max (1, xindex - xcbox / 2)
+ xhi = min (xwindow, xindex + xcbox / 2)
+ nx = xhi - xlo + 1
+ ylo = max (1, yindex - ycbox / 2)
+ yhi = min (ywindow, yindex + ycbox / 2)
+ ny = yhi - ylo + 1
+
+ # Accumulate the marginals.
+ call rg_xmkmarg (xcor, xwindow, ywindow, xlo, xhi, ylo, yhi,
+ Memr[xmarg], Memr[ymarg])
+
+ # Compute the shifts.
+ call rg_centroid (Memr[xmarg], nx, xshift)
+ xshift = xshift + xlo - 1 - (1.0 + xwindow) / 2.0
+ call rg_centroid (Memr[ymarg], ny, yshift)
+ yshift = yshift + ylo - 1 - (1.0 + ywindow) / 2.0
+
+ call sfree (sp)
+end
+
+
+# RG_IPARABOLIC -- Computer the peak of the cross-correlation function by
+# doing parabolic interpolation around the peak.
+
+procedure rg_iparabolic (xcor, xwindow, ywindow, xcbox, ycbox, xshift, yshift)
+
+real xcor[xwindow,ARB] #I the cross-correlation function
+int xwindow, ywindow #I dimensions of the cross-correlation fucntion
+int xcbox, ycbox #I the dimensions of the centering box
+real xshift, yshift #O the x and y shift of the peak
+
+int i, j, xindex, yindex, xlo, xhi, nx, ylo, yhi, ny
+pointer sp, x, y, c, xfit, yfit
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (x, 3, TY_REAL)
+ call salloc (y, 3, TY_REAL)
+ call salloc (c, 3, TY_REAL)
+ call salloc (xfit, 3, TY_REAL)
+ call salloc (yfit, 3, TY_REAL)
+
+ # Locate the maximum point.
+ call rg_alim2r (xcor, xwindow, ywindow, xindex, yindex)
+
+ xlo = max (1, xindex - 1)
+ xhi = min (xwindow, xindex + 1)
+ nx = xhi - xlo + 1
+ ylo = max (1, yindex - 1)
+ yhi = min (ywindow, yindex + 1)
+ ny = yhi - ylo + 1
+
+ # Initialize.
+ do i = 1, 3
+ Memr[x+i-1] = i
+
+ # Fit the x shift.
+ if (nx >= 3) {
+ do j = ylo, yhi {
+ do i = xlo, xhi
+ Memr[y+i-xlo] = xcor[i,j]
+ call rg_iparab (Memr[x], Memr[y], Memr[c])
+ Memr[xfit+j-ylo] = - Memr[c+1] / (2.0 * Memr[c+2])
+ Memr[yfit+j-ylo] = Memr[c] + Memr[c+1] * Memr[xfit+j-ylo] +
+ Memr[c+2] * Memr[xfit+j-ylo] ** 2
+ }
+ if (ny >= 3)
+ call rg_iparab (Memr[xfit], Memr[yfit], Memr[c])
+ xshift = - Memr[c+1] / (2.0 * Memr[c+2])
+ } else
+ xshift = xindex - xlo + 1
+
+ # Fit the y shift.
+ if (ny >= 3) {
+ do i = xlo, xhi {
+ do j = ylo, yhi
+ Memr[y+j-ylo] = xcor[i,j]
+ call rg_iparab (Memr[x], Memr[y], Memr[c])
+ Memr[xfit+i-xlo] = - Memr[c+1] / (2.0 * Memr[c+2])
+ Memr[yfit+i-xlo] = Memr[c] + Memr[c+1] * Memr[xfit+i-xlo] +
+ Memr[c+2] * Memr[xfit+i-xlo] ** 2
+ }
+ call rg_iparab (Memr[xfit], Memr[yfit], Memr[c])
+ yshift = - Memr[c+1] / (2.0 * Memr[c+2])
+ } else
+ yshift = yindex - ylo + 1
+
+ xshift = xshift + xlo - 1 - (1.0 + xwindow) / 2.0
+ yshift = yshift + ylo - 1 - (1.0 + ywindow) / 2.0
+
+ call sfree (sp)
+end
+
+
+define NPARS_PARABOLA 3
+
+# RG_PARABOLIC -- Compute the peak of the cross-correlation function by fitting
+# a parabola to the peak.
+
+procedure rg_parabolic (xcor, xwindow, ywindow, xcbox, ycbox, xshift, yshift)
+
+real xcor[xwindow,ARB] #I the cross-correlation function
+int xwindow, ywindow #I dimensions of the cross-correlation fucntion
+int xcbox, ycbox #I the dimensions of the centering box
+real xshift, yshift #O the x and y shift of the peak
+
+extern rg_polyfit, rg_dpolyfit
+int i, xindex, yindex, xlo, xhi, ylo, yhi, nx, ny, npar, ier
+pointer sp, x, w, xmarg, ymarg, params, eparams, list, nl
+int locpr()
+
+begin
+ call smark (sp)
+ call salloc (x, max (xwindow, ywindow), TY_REAL)
+ call salloc (w, max (xwindow, ywindow), TY_REAL)
+ call salloc (xmarg, max (xwindow, ywindow), TY_REAL)
+ call salloc (ymarg, max (xwindow, ywindow), TY_REAL)
+ call salloc (params, NPARS_PARABOLA, TY_REAL)
+ call salloc (eparams, NPARS_PARABOLA, TY_REAL)
+ call salloc (list, NPARS_PARABOLA, TY_INT)
+
+ # Locate the maximum point.
+ call rg_alim2r (xcor, xwindow, ywindow, xindex, yindex)
+
+ xlo = max (1, xindex - xcbox / 2)
+ xhi = min (xwindow, xindex + xcbox / 2)
+ nx = xhi - xlo + 1
+ ylo = max (1, yindex - ycbox / 2)
+ yhi = min (ywindow, yindex + ycbox / 2)
+ ny = yhi - ylo + 1
+
+ # Accumulate the marginals.
+ call rg_xmkmarg (xcor, xwindow, ywindow, xlo, xhi, ylo, yhi,
+ Memr[xmarg], Memr[ymarg])
+
+ # Compute the x shift.
+ if (nx >= 3) {
+ do i = 1, nx
+ Memr[x+i-1] = i
+ do i = 1, nx
+ Memr[w+i-1] = Memr[xmarg+i-1]
+ call rg_iparab (Memr[x+xindex-xlo-1], Memr[xmarg+xindex-xlo-1],
+ Memr[params])
+ xshift = - Memr[params+1] / (2.0 * Memr[params+2])
+ call eprintf ("\txshift=%g\n")
+ call pargr (xshift)
+ call aclrr (Memr[eparams], NPARS_PARABOLA)
+ do i = 1, NPARS_PARABOLA
+ Memi[list+i-1] = i
+ call nlinitr (nl, locpr (rg_polyfit), locpr (rg_dpolyfit),
+ Memr[params], Memr[eparams], NPARS_PARABOLA, Memi[list],
+ NPARS_PARABOLA, .0001, NL_MAXITER)
+ call nlfitr (nl, Memr[x], Memr[xmarg], Memr[w], nx, 1, WTS_USER,
+ ier)
+ call nlvectorr (nl, Memr[x], Memr[w], nx, 1)
+ do i = 1, nx {
+ call eprintf ("x=%g y=%g yfit=%g\n")
+ call pargr (Memr[x+i-1])
+ call pargr (Memr[xmarg+i-1])
+ call pargr (Memr[w+i-1])
+ }
+ if (ier != NO_DEG_FREEDOM) {
+ call nlpgetr (nl, Memr[params], npar)
+ if (Memr[params+2] != 0)
+ xshift = - Memr[params+1] / (2.0 * Memr[params+2])
+ else
+ xshift = xindex - xlo + 1
+ } else
+ xshift = xindex - xlo + 1
+ call nlfreer (nl)
+ } else
+ xshift = xindex - xlo + 1
+
+ # Compute the y shift.
+ if (ny >= 3) {
+ do i = 1, ny
+ Memr[x+i-1] = i
+ do i = 1, ny
+ Memr[w+i-1] = Memr[ymarg+i-1]
+ call rg_iparab (Memr[x+yindex-ylo-1], Memr[ymarg+yindex-ylo-1],
+ Memr[params])
+ yshift = - Memr[params+1] / (2.0 * Memr[params+2])
+ call eprintf ("\tyshift=%g\n")
+ call pargr (yshift)
+ call aclrr (Memr[eparams], NPARS_PARABOLA)
+ do i = 1, NPARS_PARABOLA
+ Memi[list+i-1] = i
+ call nlinitr (nl, locpr (rg_polyfit), locpr (rg_dpolyfit),
+ Memr[params], Memr[eparams], NPARS_PARABOLA, Memi[list],
+ NPARS_PARABOLA, 0.0001, NL_MAXITER)
+ call nlfitr (nl, Memr[x], Memr[ymarg], Memr[w], ny, 1, WTS_USER,
+ ier)
+ call nlvectorr (nl, Memr[x], Memr[w], ny, 1)
+ do i = 1, ny {
+ call eprintf ("x=%g y=%g yfit=%g\n")
+ call pargr (Memr[x+i-1])
+ call pargr (Memr[ymarg+i-1])
+ call pargr (Memr[w+i-1])
+ }
+ if (ier != NO_DEG_FREEDOM) {
+ call nlpgetr (nl, Memr[params], npar)
+ if (Memr[params+2] != 0)
+ yshift = -Memr[params+1] / (2.0 * Memr[params+2])
+ else
+ yshift = yindex - ylo + 1
+ } else
+ yshift = yindex - ylo + 1
+ call nlfreer (nl)
+ } else
+ yshift = yindex - ylo + 1
+
+ xshift = xshift + xlo - 1 - (1.0 + xwindow) / 2.0
+ yshift = yshift + ylo - 1 - (1.0 + ywindow) / 2.0
+
+ call sfree (sp)
+end
+
+define EMISSION 1 # emission features
+define ABSORPTION 2 # emission features
+
+# RG_SAWTOOTH -- Compute the the x and y centers using a sawtooth
+# convolution function.
+
+procedure rg_sawtooth (xcor, xwindow, ywindow, xcbox, ycbox, xshift, yshift)
+
+real xcor[xwindow,ARB] #I the cross-correlation function
+int xwindow, ywindow #I the dimensions of the cross-correlation
+int xcbox, ycbox #I the dimensions of the centering box
+real xshift, yshift #O the x and y shifts
+
+int i, j, xindex, yindex, xlo, xhi, ylo, yhi, nx, ny
+pointer sp, data, xfit, yfit, yclean
+real ic
+
+begin
+ call smark (sp)
+ call salloc (data, max (xwindow, ywindow), TY_REAL)
+ call salloc (xfit, max (xwindow, ywindow), TY_REAL)
+ call salloc (yfit, max (xwindow, ywindow), TY_REAL)
+ call salloc (yclean, max (xwindow, ywindow), TY_REAL)
+
+ # Locate the maximum point and normalize.
+ call rg_alim2r (xcor, xwindow, ywindow, xindex, yindex)
+
+ xlo = max (1, xindex - xcbox)
+ xhi = min (xwindow, xindex + xcbox)
+ nx = xhi - xlo + 1
+ ylo = max (1, yindex - ycbox)
+ yhi = min (ywindow, yindex + ycbox)
+ ny = yhi - ylo + 1
+
+ # Compute the y shift.
+ if (ny >= 3) {
+ do j = ylo, yhi {
+ do i = xlo, xhi
+ Memr[data+i-xlo] = xcor[i,j]
+ call rg_x1dcenter (real (xindex - xlo + 1), Memr[data], nx,
+ Memr[xfit+j-ylo], Memr[yfit+j-ylo], real (nx / 2.0),
+ EMISSION, real (nx / 2.0), 0.0)
+ }
+ call arbpix (Memr[yfit], Memr[yclean], ny, II_SPLINE3,
+ II_BOUNDARYEXT)
+ call rg_x1dcenter (real (yindex - ylo + 1), Memr[yclean], ny,
+ yshift, ic, real (ny / 2.0), EMISSION, real (ny / 2.0), 0.0)
+ if (IS_INDEFR(yshift))
+ yshift = yindex - ylo + 1
+ } else
+ yshift = yindex - ylo + 1
+ yshift = yshift + ylo - 1 - (1.0 + ywindow) / 2.0
+
+ # Compute the x shift.
+ if (nx >= 3) {
+ if (ny >= 3) {
+ do i = xlo, xhi {
+ do j = ylo, yhi
+ Memr[data+j-ylo] = xcor[i,j]
+ call rg_x1dcenter (real (yindex - ylo + 1), Memr[data], ny,
+ Memr[xfit+i-xlo], Memr[yfit+i-xlo], real (ny / 2.0),
+ EMISSION, real (ny / 2.0), 0.0)
+ }
+ call arbpix (Memr[yfit], Memr[yclean], nx, II_SPLINE3,
+ II_BOUNDARYEXT)
+ call rg_x1dcenter (real (xindex - xlo + 1), Memr[yclean], nx,
+ xshift, ic, real (nx / 2.0), EMISSION, real (nx / 2.0), 0.0)
+ } else {
+ call rg_x1dcenter (real (xindex - xlo + 1), xcor[xlo,1], nx,
+ xshift, ic, real (nx / 2.0), EMISSION, real (nx / 2.0), 0.0)
+ }
+ if (IS_INDEFR(xshift))
+ xshift = xindex - xlo + 1
+ } else
+ xshift = xindex - xlo + 1
+ xshift = xshift + xlo - 1 - (1.0 + xwindow) / 2.0
+
+ call sfree (sp)
+end
+
+
+# RG_ALIM2R -- Determine the pixel position of the data maximum.
+
+procedure rg_alim2r (data, nx, ny, i, j)
+
+real data[nx,ARB] #I the input data
+int nx, ny #I the dimensions of the input array
+int i, j #O the indices of the maximum pixel
+
+int ii, jj
+real datamax
+
+begin
+ datamax = -MAX_REAL
+ do jj = 1, ny {
+ do ii = 1, nx {
+ if (data[ii,jj] > datamax) {
+ datamax = data[ii,jj]
+ i = ii
+ j = jj
+ }
+ }
+ }
+end
+
+
+# RG_XMKMARG -- Acumulate the marginal arrays in x and y.
+
+procedure rg_xmkmarg (xcor, xwindow, ywindow, xlo, xhi, ylo, yhi, xmarg,
+ ymarg)
+
+real xcor[xwindow,ARB] #I the cross-correlation function
+int xwindow, ywindow #I dimensions of cross-correlation function
+int xlo, xhi #I the x limits for centering
+int ylo, yhi #I the y limits for centering
+real xmarg[ARB] #O the output x marginal array
+real ymarg[ARB] #O the output y marginal array
+
+int i, j, index, nx, ny
+
+begin
+ nx = xhi - xlo + 1
+ ny = yhi - ylo + 1
+
+ # Compute the x marginal.
+ index = 1 - xlo
+ do i = xlo, xhi {
+ xmarg[index+i] = 0.0
+ do j = ylo, yhi
+ xmarg[index+i] = xmarg[index+i] + xcor[i,j]
+ }
+
+ # Normalize the x marginal.
+ call adivkr (xmarg, real (ny), xmarg, nx)
+
+ # Compute the y marginal.
+ index = 1 - ylo
+ do j = ylo, yhi {
+ ymarg[index+j] = 0.0
+ do i = xlo, xhi
+ ymarg[index+j] = ymarg[index+j] + xcor[i,j]
+ }
+
+ # Normalize the ymarginal.
+ call adivkr (ymarg, real (nx), ymarg, ny)
+end
+
+
+# RG_CENTROID -- Compute the intensity weighted maximum of an array.
+
+procedure rg_centroid (a, npts, shift)
+
+real a[ARB] #I the input array
+int npts #I the number of points
+real shift #O the position of the maximum
+
+int i
+real mean, dif, sumi, sumix
+bool fp_equalr()
+real asumr()
+
+begin
+ sumi = 0.0
+ sumix = 0.0
+ mean = asumr (a, npts) / npts
+
+ do i = 1, npts {
+ dif = a[i]
+ dif = a[i] - mean
+ if (dif < 0.0)
+ next
+ sumi = sumi + dif
+ sumix = sumix + i * dif
+ }
+
+ if (fp_equalr (sumi, 0.0))
+ shift = (1.0 + npts) / 2.0
+ else
+ shift = sumix / sumi
+end
+
+
+define MIN_WIDTH 3. # minimum centering width
+define EPSILON 0.001 # accuracy of centering
+define EPSILON1 0.005 # tolerance for convergence check
+define ITERATIONS 100 # maximum number of iterations
+define MAX_DXCHECK 3 # look back for failed convergence
+define INTERPTYPE II_SPLINE3 # image interpolation type
+
+
+# RG_X1DCENTER -- Locate the center of a one dimensional feature.
+# A value of INDEF is returned in the centering fails for any reason.
+# This procedure just sets up the data and adjusts for emission or
+# absorption features. The actual centering is done by C1D_CENTER.
+
+procedure rg_x1dcenter (x, data, npts, xc, ic, width, type, radius, threshold)
+
+real x #I initial guess
+real data[npts] #I data points
+int npts #I number of data points
+real xc #O computed center
+real ic #O intensity at computed center
+real width #I feature width
+int type #I feature type
+real radius #I centering radius
+real threshold #I minimum range in feature
+
+int x1, x2, nx
+real a, b, rad, wid
+pointer sp, data1
+
+begin
+ # Check starting value.
+ if (IS_INDEF(x) || (x < 1) || (x > npts)) {
+ xc = INDEF
+ ic = INDEF
+ return
+ }
+
+ # Set minimum width and error radius. The minimum in the error radius
+ # is for defining the data window. The user error radius is used to
+ # check for an error in the derived center at the end of the centering.
+
+ wid = max (width, MIN_WIDTH)
+ rad = max (2., radius)
+
+ # Determine the pixel value range around the initial center, including
+ # the width and error radius buffer. Check for a minimum range.
+
+ x1 = max (1., x - wid / 2 - rad - wid)
+ x2 = min (real (npts), x + wid / 2 + rad + wid + 1)
+ nx = x2 - x1 + 1
+ call alimr (data[x1], nx, a, b)
+ if (b - a < threshold) {
+ xc = INDEF
+ ic = INDEF
+ return
+ }
+
+ # Allocate memory for the continuum subtracted data vector. The X
+ # range is just large enough to include the error radius and the
+ # half width.
+
+ x1 = max (1., x - wid / 2 - rad)
+ x2 = min (real (npts), x + wid / 2 + rad + 1)
+ nx = x2 - x1 + 1
+
+ call smark (sp)
+ call salloc (data1, nx, TY_REAL)
+ call amovr (data[x1], Memr[data1], nx)
+
+ # Make the centering data positive, subtract the continuum, and
+ # apply a threshold to eliminate noise spikes.
+
+ switch (type) {
+ case EMISSION:
+ a = 0.
+ call asubkr (data[x1], a + threshold, Memr[data1], nx)
+ call amaxkr (Memr[data1], 0., Memr[data1], nx)
+ case ABSORPTION:
+ call anegr (data[x1], Memr[data1], nx)
+ call asubkr (Memr[data1], threshold - b, Memr[data1], nx)
+ call amaxkr (Memr[data1], 0., Memr[data1], nx)
+ default:
+ call error (0, "Unknown feature type")
+ }
+
+ # Determine the center.
+ call rg_xcenter (x - x1 + 1, Memr[data1], nx, xc, ic, wid)
+
+ # Check user centering error radius.
+ if (!IS_INDEF(xc)) {
+ xc = xc + x1 - 1
+ if (abs (x - xc) > radius) {
+ xc = INDEF
+ ic = INDEF
+ }
+ }
+
+ # Free memory and return the center position.
+ call sfree (sp)
+end
+
+
+# RG_XCENTER -- One dimensional centering algorithm.
+
+procedure rg_xcenter (x, data, npts, xc, ic, width)
+
+real x #I starting guess
+int npts #I number of points in data vector
+real data[npts] #I data vector
+real xc #O computed xc
+real ic #O computed intensity at xc
+real width #I centering width
+
+int i, j, iteration, dxcheck
+real hwidth, dx, dxabs, dxlast
+real a, b, sum1, sum2, intgrl1, intgrl2
+pointer asi1, asi2, sp, data1
+
+real asigrl(), asieval()
+
+define done_ 99
+
+begin
+ # Find the nearest local maxima as the starting point.
+ # This is required because the threshold limit may have set
+ # large regions of the data to zero and without a gradient
+ # the centering will fail.
+
+ i = x
+ for (i=x+.5; (i<npts) && (data[i]<=data[i+1]); i=i+1)
+ ;
+ for (j=x+.5; (j>1) && (data[j]<=data[j-1]); j=j-1)
+ ;
+
+ if (i-x < x-j)
+ xc = i
+ else
+ xc = j
+
+ # Check data range.
+ hwidth = width / 2
+ if ((xc - hwidth < 1) || (xc + hwidth > npts)) {
+ xc = INDEF
+ ic = INDEF
+ return
+ }
+
+ # Set interpolation functions.
+ call asiinit (asi1, INTERPTYPE)
+ call asiinit (asi2, INTERPTYPE)
+ call asifit (asi1, data, npts)
+
+ # Allocate, compute, and interpolate the x*y values.
+ call smark (sp)
+ call salloc (data1, npts, TY_REAL)
+ do i = 1, npts
+ Memr[data1+i-1] = data[i] * i
+ call asifit (asi2, Memr[data1], npts)
+ call sfree (sp)
+
+ # Iterate to find center. This loop exits when 1) the maximum
+ # number of iterations is reached, 2) the delta is less than
+ # the required accuracy (criterion for finding a center), 3)
+ # there is a problem in the computation, 4) successive steps
+ # continue to exceed the minimum delta.
+
+ dxlast = 1.
+ do iteration = 1, ITERATIONS {
+
+ # Triangle centering function.
+ a = xc - hwidth
+ b = xc - hwidth / 2
+ intgrl1 = asigrl (asi1, a, b)
+ intgrl2 = asigrl (asi2, a, b)
+ sum1 = (xc - hwidth) * intgrl1 - intgrl2
+ sum2 = -intgrl1
+ a = b
+ b = xc + hwidth / 2
+ intgrl1 = asigrl (asi1, a, b)
+ intgrl2 = asigrl (asi2, a, b)
+ sum1 = sum1 - xc * intgrl1 + intgrl2
+ sum2 = sum2 + intgrl1
+ a = b
+ b = xc + hwidth
+ intgrl1 = asigrl (asi1, a, b)
+ intgrl2 = asigrl (asi2, a, b)
+ sum1 = sum1 + (xc + hwidth) * intgrl1 - intgrl2
+ sum2 = sum2 - intgrl1
+
+ # Return no center if sum2 is zero.
+ if (sum2 == 0.)
+ break
+
+ # Limit dx change in one iteration to 1 pixel.
+ dx = max (-1., min (1., sum1 / abs (sum2)))
+ dxabs = abs (dx)
+ xc = xc + dx
+ ic = asieval (asi1, xc)
+
+ # Check data range. Return no center if at edge of data.
+ if ((xc - hwidth < 1) || (xc + hwidth > npts))
+ break
+
+ # Convergence tests.
+ if (dxabs < EPSILON)
+ goto done_
+ if (dxabs > dxlast + EPSILON1) {
+ dxcheck = dxcheck + 1
+ if (dxcheck > MAX_DXCHECK)
+ break
+ } else {
+ dxcheck = 0
+ dxlast = dxabs
+ }
+ }
+
+ # If we get here then no center was found.
+ xc = INDEF
+ ic = INDEF
+
+done_ call asifree (asi1)
+ call asifree (asi2)
+end
+
+
+# RG_IPARAB -- Compute the coefficients of the parabola through three
+# evenly spaced points.
+
+procedure rg_iparab (x, y, c)
+
+real x[NPARS_PARABOLA] #I input x values
+real y[NPARS_PARABOLA] #I input y values
+real c[NPARS_PARABOLA] #O computed coefficients
+
+begin
+ c[3] = (y[1]-y[2]) * (x[2]-x[3]) / (x[1]-x[2]) - (y[2]-y[3])
+ c[3] = c[3] / ((x[1]**2-x[2]**2) * (x[2]-x[3]) / (x[1]-x[2]) -
+ (x[2]**2-x[3]**2))
+
+ c[2] = (y[1] - y[2]) - c[3] * (x[1]**2 - x[2]**2)
+ c[2] = c[2] / (x[1] - x[2])
+
+ c[1] = y[1] - c[2] * x[1] - c[3] * x[1]**2
+end
+
+
+# RG_POLYFIT -- Evaluate an nth order polynomial.
+
+procedure rg_polyfit (x, nvars, p, np, z)
+
+real x #I position coordinate
+int nvars #I number of variables
+real p[ARB] #I coefficients of polynomial
+int np #I number of parameters
+real z #O function return
+
+int i
+real r
+
+begin
+ r = 0.0
+ do i = 2, np
+ r = r + x**(i-1) * p[i]
+ z = p[1] + r
+end
+
+
+# RG_DPOLYFIT -- Evaluate an nth order polynomial and its derivatives.
+
+procedure rg_dpolyfit (x, nvars, p, dp, np, z, der)
+
+real x #I position coordinate
+int nvars #I number of variables
+real p[ARB] #I coefficients of polynomial
+real dp[ARB] #I parameter derivative increments
+int np #I number of parameters
+real z #O function value
+real der[ARB] #O derivatives
+
+int i
+
+begin
+ der[1] = 1.0
+ z = 0.0
+ do i = 2, np {
+ der[i] = x ** (i-1)
+ z = z + x**(i-1) * p[i]
+ }
+ z = p[1] + z
+end
diff --git a/pkg/images/immatch/src/xregister/rgxgpars.x b/pkg/images/immatch/src/xregister/rgxgpars.x
new file mode 100644
index 00000000..82943730
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxgpars.x
@@ -0,0 +1,68 @@
+include "xregister.h"
+
+# RG_XGPARS -- Read in the XREGISTER task algorithm parameters.
+
+procedure rg_xgpars (xc)
+
+pointer xc #I pointer to the main structure
+
+int xlag, ylag, xwindow, ywindow, xcbox, ycbox
+pointer sp, str
+int clgwrd(), clgeti()
+real clgetr()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Initialize the correlation structure.
+ call rg_xinit (xc, clgwrd ("correlation", Memc[str], SZ_LINE,
+ XC_CTYPES))
+
+ # Fetch the initial shift information.
+ xlag = clgeti ("xlag")
+ ylag = clgeti ("ylag")
+ call rg_xseti (xc, IXLAG, xlag)
+ call rg_xseti (xc, IYLAG, ylag)
+ call rg_xseti (xc, XLAG, xlag)
+ call rg_xseti (xc, YLAG, ylag)
+ call rg_xseti (xc, DXLAG, clgeti ("dxlag"))
+ call rg_xseti (xc, DYLAG, clgeti ("dylag"))
+
+ # Get the background value computation parameters.
+ call rg_xseti (xc, BACKGRD, clgwrd ("background", Memc[str], SZ_LINE,
+ XC_BTYPES))
+ call rg_xsets (xc, BSTRING, Memc[str])
+ call rg_xseti (xc, BORDER, clgeti ("border"))
+ call rg_xsetr (xc, LOREJECT, clgetr ("loreject"))
+ call rg_xsetr (xc, HIREJECT, clgetr ("hireject"))
+ call rg_xsetr (xc, APODIZE, clgetr ("apodize"))
+ call rg_xseti (xc, FILTER, clgwrd ("filter", Memc[str], SZ_LINE,
+ XC_FTYPES))
+ call rg_xsets (xc, FSTRING, Memc[str])
+
+ # Get the window parameters and force the window size to be odd.
+ xwindow = clgeti ("xwindow")
+ if (mod (xwindow,2) == 0)
+ xwindow = xwindow + 1
+ call rg_xseti (xc, XWINDOW, xwindow)
+ ywindow = clgeti ("ywindow")
+ if (mod (ywindow,2) == 0)
+ ywindow = ywindow + 1
+ call rg_xseti (xc, YWINDOW, ywindow)
+
+ # Get the peak fitting parameters.
+ call rg_xseti (xc, PFUNC, clgwrd ("function", Memc[str], SZ_LINE,
+ XC_PTYPES))
+ xcbox = clgeti ("xcbox")
+ if (mod (xcbox,2) == 0)
+ xcbox = xcbox + 1
+ call rg_xseti (xc, XCBOX, xcbox)
+ ycbox = clgeti ("ycbox")
+ if (mod (ycbox,2) == 0)
+ ycbox = ycbox + 1
+ call rg_xseti (xc, YCBOX, ycbox)
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/xregister/rgxicorr.x b/pkg/images/immatch/src/xregister/rgxicorr.x
new file mode 100644
index 00000000..e96c6dec
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxicorr.x
@@ -0,0 +1,583 @@
+include <imhdr.h>
+include <fset.h>
+include <ctype.h>
+include "xregister.h"
+
+define HELPFILE "immatch$src/xregister/xregister.key"
+define OHELPFILE "immatch$src/xregister/oxregister.key"
+
+define XC_PCONTOUR 1
+define XC_PLINE 2
+define XC_PCOL 3
+
+
+# RG_XICORR -- Compute the shifts for each image interactively using
+# cross-correlation techniques.
+
+int procedure rg_xicorr (imr, im1, im2, db, dformat, reglist, tfd, xc, gd, id)
+
+pointer imr #I/O pointer to the reference image
+pointer im1 #I/O pointer to the input image
+pointer im2 #I/O pointer to the output image
+pointer db #I/O pointer to the shifts database file
+int dformat #I is the shifts file in database format
+int reglist #I/O the regions list descriptor
+int tfd #I/O the transform file descriptor
+pointer xc #I pointer to the cross-corrrelation structure
+pointer gd #I the graphics stream pointer
+pointer id #I the display stream pointer
+
+int newdata, newcross, newcenter, wcs, key, cplottype, newplot
+int ip, ncolr, nliner
+pointer sp, cmd
+real xshift, yshift, wx, wy
+int rg_xstati(), rg_icross(), clgcur(), rg_xgtverify(), rg_xgqverify()
+int ctoi()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Initialize.
+ newdata = YES
+ newcross = YES
+ newcenter = YES
+ ncolr = (1 + rg_xstati (xc, XWINDOW)) / 2
+ nliner = (1 + rg_xstati (xc, YWINDOW)) / 2
+ cplottype = XC_PCONTOUR
+ newplot = YES
+ xshift = 0.0
+ yshift = 0.0
+
+ # Compute the cross-correlation function for the first region
+ # and print the results.
+ if (rg_xstati (xc, NREGIONS) <= 0) {
+ call gclear (gd)
+ call printf ("The regions list is empty\n")
+ } else if (rg_icross (xc, imr, im1, rg_xstati (xc, CREGION)) != ERR) {
+ call rg_xcplot (xc, gd, ncolr, nliner, cplottype)
+ call rg_fit (xc, rg_xstati (xc, CREGION), gd, xshift, yshift)
+ call rg_xpwrec (xc, rg_xstati (xc, CREGION))
+ newdata = NO
+ newcross = NO
+ newcenter = NO
+ newplot = NO
+ } else {
+ call gclear (gd)
+ call printf (
+ "Error computing X-correlation function for region %d\n")
+ call pargi (rg_xstati (xc, CREGION))
+ }
+
+
+ # Loop over the cursor commands.
+ while (clgcur ("gcommands", wx, wy, wcs, key, Memc[cmd], SZ_LINE) !=
+ EOF) {
+
+ switch (key) {
+
+ # Print the help page.
+ case '?':
+ call gpagefile (gd, HELPFILE, "")
+
+ # Redraw the current plot.
+ case 'r':
+ newplot = YES
+
+ # Draw a contour plot of the cross-correlation function.
+ case 'c':
+ if (cplottype != XC_PCONTOUR)
+ newplot = YES
+ ncolr = (rg_xstati (xc, XWINDOW) + 1) / 2
+ nliner = (rg_xstati (xc, YWINDOW) + 1) / 2
+ cplottype = XC_PCONTOUR
+
+ # Plot a column of the cross-correlation function.
+ case 'x':
+ if (cplottype != XC_PCOL)
+ newplot = YES
+ if (cplottype == XC_PCONTOUR) {
+ ncolr = nint (wx)
+ nliner = nint (wy)
+ } else if (cplottype == XC_PLINE) {
+ ncolr = nint (wx)
+ }
+ cplottype = XC_PCOL
+
+ # Plot a line of the cross-correlation function.
+ case 'y':
+ if (cplottype != XC_PLINE)
+ newplot = YES
+ if (cplottype == XC_PCONTOUR) {
+ ncolr = nint (wx)
+ nliner = nint (wy)
+ } else if (cplottype == XC_PCOL) {
+ ncolr = nint (wx)
+ }
+ cplottype = XC_PLINE
+
+ # Quit the task gracefully.
+ case 'q':
+ if (rg_xgqverify ("xregister", db, dformat, xc, key) == YES) {
+ call sfree (sp)
+ return (rg_xgtverify (key))
+ }
+
+ # The Data overlay menu.
+ case 'o':
+ #call gdeactivate (gd, 0)
+ call rg_xoverlay (gd, xc, rg_xstati (xc, CREGION), imr, im1)
+ #call greactivate (gd, 0)
+ newplot = YES
+
+ # Process colon commands.
+ case ':':
+ for (ip = 1; IS_WHITE(Memc[cmd+ip-1]); ip = ip + 1)
+ ;
+ switch (Memc[cmd+ip-1]) {
+ case 'x':
+ if (Memc[cmd+ip] != EOS && Memc[cmd+ip] != ' ') {
+ call rg_xcolon (gd, xc, imr, im1, im2, db, dformat,
+ tfd, reglist, Memc[cmd], newdata, newcross,
+ newcenter)
+ } else {
+ ip = ip + 1
+ if (ctoi (Memc[cmd], ip, ncolr) <= 0)
+ ncolr = (1 + rg_xstati (xc, XWINDOW)) / 2
+ cplottype = XC_PCOL
+ newplot = YES
+ }
+ case 'y':
+ if (Memc[cmd+ip] != EOS && Memc[cmd+ip] != ' ') {
+ call rg_xcolon (gd, xc, imr, im1, im2, db, dformat,
+ tfd, reglist, Memc[cmd], newdata, newcross,
+ newcenter)
+ } else {
+ ip = ip + 1
+ if (ctoi (Memc[cmd], ip, nliner) <= 0)
+ nliner = (1 + rg_xstati (xc, YWINDOW)) / 2
+ cplottype = XC_PLINE
+ newplot = YES
+ }
+ default:
+ call rg_xcolon (gd, xc, imr, im1, im2, db, dformat, tfd,
+ reglist, Memc[cmd], newdata, newcross, newcenter)
+ }
+
+ # Compute an image lag interactively.
+ case 't':
+ call gdeactivate (gd, 0)
+ call rg_itransform (xc, imr, im1, id)
+ newdata = YES; newcross = YES; newcenter = YES
+ call greactivate (gd, 0)
+
+ # Write the parameters to the parameter file.
+ case 'w':
+ call rg_pxpars (xc)
+
+ case 'f':
+
+ if (rg_xstati (xc, NREGIONS) > 0) {
+
+ if (newdata == YES) {
+ call rg_xcindefr (xc, rg_xstati(xc,CREGION))
+ newdata = NO
+ }
+
+ if (newcross == YES) {
+ call printf (
+ "Recomputing X-correlation function ...\n")
+ if (rg_icross (xc, imr, im1, rg_xstati (xc,
+ CREGION)) != ERR) {
+ ncolr = (1 + rg_xstati (xc, XWINDOW)) / 2
+ if (IM_NDIM(imr) == 1)
+ nliner = 1
+ else
+ nliner = (1 + rg_xstati (xc, YWINDOW)) / 2
+ call rg_xcplot (xc, gd, ncolr, nliner, cplottype)
+ call rg_fit (xc, rg_xstati (xc, CREGION), gd,
+ xshift, yshift)
+ call rg_xpwrec (xc, rg_xstati (xc, CREGION))
+ newcross = NO
+ newcenter = NO
+ newplot = NO
+ } else {
+ call printf (
+ "Error computing X-correlation function for region %d\n")
+ call pargi (rg_xstati (xc, CREGION))
+ }
+ }
+
+ if (newcenter == YES) {
+ call rg_fit (xc, rg_xstati (xc, CREGION), gd,
+ xshift, yshift)
+ call rg_xpwrec (xc, rg_xstati (xc, CREGION))
+ newcenter = NO
+ }
+
+ } else
+ call printf ("The regions list is empty\n")
+
+
+
+ # Do nothing gracefully.
+ default:
+ call printf ("Unknown or ambiguous keystroke command\n")
+ }
+
+ # Replot the correlation function.
+ if (newplot == YES) {
+ if (newdata == YES) {
+ call printf (
+ "Warning: X-correlation function should be refit\n")
+ } else if (newcross == YES) {
+ call printf (
+ "Warning: X-correlation function should be refit\n")
+ } else if (newcenter == YES) {
+ call printf (
+ "Warning: X-correlation function should be refit\n")
+ } else if (rg_xstatp (xc, XCOR) != NULL) {
+ call rg_xcplot (xc, gd, ncolr, nliner, cplottype)
+ call rg_xpwrec (xc, rg_xstati (xc, CREGION))
+ newplot = NO
+ } else {
+ call printf (
+ "Warning: X-correlation function is undefined\n")
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_XOVERLAY -- The image overlay plot menu.
+
+procedure rg_xoverlay (gd, xc, nreg, imr, im1)
+
+pointer gd #I graphics stream pointer
+pointer xc #I pointer to the crosscor structure
+int nreg #I the current region number
+pointer imr #I pointer to the reference image
+pointer im1 #I pointer to the input image
+
+int ip, wcs, key, ixlag, iylag, ixshift, iyshift
+int nrimcols, nrimlines, nimcols, nimlines, ncolr, ncoli, nliner, nlinei
+pointer sp, cmd
+real wx, wy, rxlag, rylag, xshift, yshift
+int clgcur(), ctoi(), rg_xstati()
+pointer rg_xstatp()
+
+begin
+ if (gd == NULL)
+ return
+
+ nrimcols = IM_LEN(imr,1)
+ if (IM_NDIM(imr) == 1)
+ nrimlines = 1
+ else
+ nrimlines = IM_LEN(imr,2)
+ nimcols = IM_LEN(im1,1)
+ if (IM_NDIM(im1) == 1)
+ nimlines = 1
+ else
+ nimlines = IM_LEN(im1,2)
+ if (rg_xstati (xc, NREFPTS) > 0) {
+ wx = (1. + nrimcols) / 2.0
+ wy = (1. + nrimlines) / 2.0
+ call rg_etransform (xc, wx, wy, rxlag, rylag)
+ ixlag = rxlag - wx
+ iylag = rylag - wy
+ } else {
+ ixlag = rg_xstati (xc, XLAG)
+ iylag = rg_xstati (xc, YLAG)
+ }
+ xshift = -Memr[rg_xstatp(xc,XSHIFTS)+nreg-1]
+ yshift = -Memr[rg_xstatp(xc,YSHIFTS)+nreg-1]
+
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ while (clgcur ("icommands", wx, wy, wcs, key, Memc[cmd],
+ SZ_LINE) != EOF) {
+
+ switch (key) {
+
+ # Print the help menu.
+ case '?':
+ call gdeactivate (gd, 0)
+ call pagefile (OHELPFILE, "")
+ call greactivate (gd, 0)
+
+ # Quit.
+ case 'q':
+ break
+
+ # Plot the same line of the reference and input image.
+ case 'l':
+ call rg_xpline (gd, imr, im1, nint (wy), 0, 0)
+
+ # Plot the same column of the reference and input image
+ case 'c':
+ call rg_xpcol (gd, imr, im1, nint (wx), 0, 0)
+
+ case 'y':
+ call rg_xpline (gd, imr, im1, nint (wy), ixlag, iylag)
+
+ case 'x':
+ call rg_xpcol (gd, imr, im1, nint (wx), ixlag, iylag)
+
+ case 'h':
+ call rg_xpline (gd, imr, im1, nint (wy), nint (xshift),
+ nint (yshift))
+
+ case 'v':
+ call rg_xpcol (gd, imr, im1, nint (wx), nint (xshift),
+ nint (yshift))
+
+ case ':':
+ ip = 1
+ call rg_cokeys (Memc[cmd], ip, SZ_LINE, key)
+ switch (key) {
+ case 'l':
+ ixshift = 0
+ if (ctoi (Memc[cmd], ip, nliner) <= 0)
+ nliner = (1 + nrimlines) / 2
+ nliner = max (1, min (nliner, nrimlines))
+ if (ctoi (Memc[cmd], ip, nlinei) <= 0)
+ nlinei = nliner
+ iyshift = nlinei - nliner
+ call rg_xpline (gd, imr, im1, nliner, ixshift, iyshift)
+
+ case 'c':
+ if (ctoi (Memc[cmd], ip, ncolr) <= 0)
+ ncolr = (1 + nrimcols) / 2
+ ncolr = max (1, min (ncolr, nrimcols))
+ if (ctoi (Memc[cmd], ip, ncoli) <= 0)
+ ncoli = ncolr
+ ncoli = max (1, min (ncoli, nimcols))
+ ixshift = ncoli - ncolr
+ iyshift = 0
+ call rg_xpcol (gd, imr, im1, ncolr, ixshift, iyshift)
+
+ case 'y':
+ if (ctoi (Memc[cmd], ip, nliner) <= 0)
+ nliner = (1 + nrimlines) / 2
+ nliner = max (1, min (nliner, nrimlines))
+ call rg_xpline (gd, imr, im1, nliner, ixlag, iylag)
+
+ case 'x':
+ if (ctoi (Memc[cmd], ip, ncolr) <= 0)
+ ncolr = (1 + nrimcols) / 2
+ ncolr = max (1, min (ncolr, nrimcols))
+ call rg_xpcol (gd, imr, im1, ncolr, ixlag, iylag)
+
+ case 'h':
+ if (ctoi (Memc[cmd], ip, nliner) <= 0)
+ nliner = (1 + nrimlines) / 2
+ nliner = max (1, min (nliner, nrimlines))
+ call rg_xpline (gd, imr, im1, nliner, nint (xshift),
+ nint (yshift))
+
+ case 'v':
+ if (ctoi (Memc[cmd], ip, ncolr) <= 0)
+ ncolr = (1 + nrimcols) / 2
+ ncolr = max (1, min (ncolr, nrimcols))
+ call rg_xpcol (gd, imr, im1, ncolr, nint (xshift),
+ nint (yshift))
+ default:
+ call printf ("Ambiguous or unknown overlay menu command\n")
+ }
+ case 'g':
+ while (clgcur ("gcommands", wx, wy, wcs, key, Memc[cmd],
+ SZ_LINE) != EOF) {
+ if (key == 'q')
+ break
+ }
+ default:
+ call printf ("Ambiguous or unknown overlay menu command\n")
+ }
+
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_XCPLOT -- Draw the default plot of the cross-correlation function.
+
+procedure rg_xcplot (xc, gd, col, line, plottype)
+
+pointer xc #I pointer to cross-correlation structure
+pointer gd #I pointer to the graphics stream
+int col #I column of cross-correlation function to plot
+int line #I line of cross-correlation function to plot
+int plottype #I the default plot type
+
+int nreg, xwindow, ywindow
+pointer sp, title, str, prc1, prc2, prl1, prl2
+int rg_xstati(), strlen()
+pointer rg_xstatp()
+
+begin
+ if (gd == NULL)
+ return
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (title, SZ_LINE, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get the regions.
+ nreg = rg_xstati (xc, CREGION)
+ prc1 = rg_xstatp (xc, RC1)
+ prc2 = rg_xstatp (xc, RC2)
+ prl1 = rg_xstatp (xc, RL1)
+ prl2 = rg_xstatp (xc, RL2)
+
+ # Initialize the window size.
+ xwindow = rg_xstati (xc, XWINDOW)
+ if ((Memi[prl2+nreg-1] - Memi[prl1+nreg-1] + 1) == 1)
+ ywindow = 1
+ else
+ ywindow = rg_xstati (xc, YWINDOW)
+
+ # Construct a title.
+ call sprintf (Memc[title], SZ_LINE,
+ "Reference: %s Image: %s Region: [%d:%d,%d:%d]")
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_FNAME)
+ call pargstr (Memc[str])
+ call pargi (Memi[prc1+nreg-1])
+ call pargi (Memi[prc2+nreg-1])
+ call pargi (Memi[prl1+nreg-1])
+ call pargi (Memi[prl2+nreg-1])
+
+ # Draw the plot.
+ if (ywindow == 1) {
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nX-Correlation Function: line %d")
+ call pargi (1)
+ call rg_xcpline (gd, Memc[title], Memr[rg_xstatp(xc,XCOR)],
+ xwindow, ywindow, 1)
+ } else {
+ switch (plottype) {
+ case XC_PCONTOUR:
+ call rg_contour (gd, "X-Correlation Function", Memc[title],
+ Memr[rg_xstatp (xc, XCOR)], xwindow, ywindow)
+ case XC_PLINE:
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nX-Correlation Function: line %d")
+ call pargi (line)
+ call rg_xcpline (gd, Memc[title], Memr[rg_xstatp(xc,XCOR)],
+ xwindow, ywindow, line)
+ case XC_PCOL:
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "\nX-Correlation Function: column %d")
+ call pargi (col)
+ call rg_xcpcol (gd, Memc[title], Memr[rg_xstatp(xc,XCOR)],
+ xwindow, ywindow, col)
+ default:
+ call rg_contour (gd, "X-Correlation Function", Memc[title],
+ Memr[rg_xstatp (xc, XCOR)], xwindow, ywindow)
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_COKEYS -- Fetch the first keystroke of a colon command.
+
+procedure rg_cokeys (cmd, ip, maxch, key)
+
+char cmd[ARB] #I the command string
+int ip #I/O pointer into the command string
+int maxch #I maximum number of characters
+int key #O the keystroke
+
+begin
+ ip = 1
+ while (IS_WHITE(cmd[ip]) && cmd[ip] != EOS && ip <= maxch)
+ ip = ip + 1
+
+ if (cmd[ip] == EOS && ip > maxch)
+ key = EOS
+ else {
+ key = cmd[ip]
+ ip = ip + 1
+ }
+end
+
+
+define QUERY "Hit [return=continue, n=next image, q=quit, w=quit and update parameters]: "
+
+# RG_XGQVERIFY -- Print a message on the status line asking the user if they
+# really want to quit, returning YES if they really want to quit, NO otherwise.
+
+int procedure rg_xgqverify (task, db, dformat, rg, ch)
+
+char task[ARB] #I the calling task name
+pointer db #I pointer to the shifts database file
+int dformat #I is the shifts file in database format
+pointer rg #I pointer to the task structure
+int ch #I the input keystroke command
+
+int wcs, stat
+pointer sp, cmd
+real wx, wy
+bool streq()
+int clgcur()
+
+begin
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Print the status line query in reverse video and get the keystroke.
+ call printf (QUERY)
+ #call flush (STDOUT)
+ if (clgcur ("gcommands", wx, wy, wcs, ch, Memc[cmd], SZ_LINE) == EOF)
+ ;
+
+ # Process the command.
+ if (ch == 'q') {
+ call rg_xwrec (db, dformat, rg)
+ stat = YES
+ } else if (ch == 'w') {
+ call rg_xwrec (db, dformat, rg)
+ if (streq ("xregister", task))
+ call rg_pxpars (rg)
+ stat = YES
+ } else if (ch == 'n') {
+ call rg_xwrec (db, dformat, rg)
+ stat = YES
+ } else {
+ stat = NO
+ }
+
+ call sfree (sp)
+ return (stat)
+end
+
+
+# RG_XGTVERIFY -- Verify whether or not the user truly wishes to quit the
+# task.
+
+int procedure rg_xgtverify (ch)
+
+int ch #I the input keystroke command
+
+begin
+ if (ch == 'q') {
+ return (YES)
+ } else if (ch == 'w') {
+ return (YES)
+ } else if (ch == 'n') {
+ return (NO)
+ } else {
+ return (NO)
+ }
+end
diff --git a/pkg/images/immatch/src/xregister/rgximshift.x b/pkg/images/immatch/src/xregister/rgximshift.x
new file mode 100644
index 00000000..08cb3f62
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgximshift.x
@@ -0,0 +1,391 @@
+include <imhdr.h>
+include <imset.h>
+include <math/iminterp.h>
+
+define NYOUT 16 # number of lines output at once
+define NMARGIN 3 # number of boundary pixels required
+define NMARGIN_SPLINE3 16 # number of spline boundary pixels required
+
+
+# RG_XSHIFTIM - Shift a 1 or 2D image by a fractional pixel amount
+# x and y
+
+procedure rg_xshiftim (im1, im2, xshift, yshift, interpstr, boundary_type,
+ constant)
+
+pointer im1 #I pointer to input image
+pointer im2 #I pointer to output image
+real xshift #I shift in x direction
+real yshift #I shift in y direction
+char interpstr[ARB] #I type of interpolant
+int boundary_type #I type of boundary extension
+real constant #I value of constant for boundary extension
+
+int interp_type
+pointer sp, str
+bool fp_equalr()
+int strdic()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_FNAME, TY_CHAR)
+ interp_type = strdic (interpstr, Memc[str], SZ_FNAME, II_BFUNCTIONS)
+
+ if (interp_type == II_NEAREST)
+ call rg_xishiftim (im1, im2, nint (xshift), nint (yshift),
+ interp_type, boundary_type, constant)
+ else if (fp_equalr (xshift, real (int (xshift))) && fp_equalr (yshift,
+ real (int (xshift))))
+ call rg_xishiftim (im1, im2, int (xshift), int (yshift),
+ interp_type, boundary_type, constant)
+ else
+ call rg_xfshiftim (im1, im2, xshift, yshift, interpstr,
+ boundary_type, constant)
+ call sfree (sp)
+end
+
+
+# RG_XISHIFTIM -- Shift a 2-D image by integral pixels in x and y.
+
+procedure rg_xishiftim (im1, im2, nxshift, nyshift, interp_type, boundary_type,
+ constant)
+
+pointer im1 #I pointer to the input image
+pointer im2 #I pointer to the output image
+int nxshift, nyshift #I shift in x and y
+int interp_type #I type of interpolant
+int boundary_type #I type of boundary extension
+real constant #I constant for boundary extension
+
+int ixshift, iyshift
+pointer buf1, buf2
+long v[IM_MAXDIM]
+int ncols, nlines, nbpix
+int i, x1col, x2col, yline
+
+int impnls(), impnli(), impnll(), impnlr(), impnld(), impnlx()
+pointer imgs2s(), imgs2i(), imgs2l(), imgs2r(), imgs2d(), imgs2x()
+errchk impnls, impnli, impnll, impnlr, impnld, impnlx
+errchk imgs2s, imgs2i, imgs2l, imgs2r, imgs2d, imgs2x
+string wrerr "ISHIFTXY: Error writing in image."
+
+begin
+ ixshift = nxshift
+ iyshift = nyshift
+
+ ncols = IM_LEN(im1,1)
+ nlines = IM_LEN(im1,2)
+
+ # Cannot shift off image.
+ if (ixshift < -ncols || ixshift > ncols)
+ call error (3, "ISHIFTXY: X shift out of bounds.")
+ if (iyshift < -nlines || iyshift > nlines)
+ call error (4, "ISHIFTXY: Y shift out of bounds.")
+
+ # Calculate the shift.
+ switch (boundary_type) {
+ case BT_CONSTANT,BT_REFLECT,BT_NEAREST:
+ ixshift = min (ncols, max (-ncols, ixshift))
+ iyshift = min (nlines, max (-nlines, iyshift))
+ case BT_WRAP:
+ ixshift = mod (ixshift, ncols)
+ iyshift = mod (iyshift, nlines)
+ }
+
+ # Set the boundary extension values.
+ nbpix = max (abs (ixshift), abs (iyshift))
+ call imseti (im1, IM_NBNDRYPIX, nbpix)
+ call imseti (im1, IM_TYBNDRY, boundary_type)
+ if (boundary_type == BT_CONSTANT)
+ call imsetr (im1, IM_BNDRYPIXVAL, constant)
+
+ # Get column boundaries in the input image.
+ x1col = max (-ncols + 1, - ixshift + 1)
+ x2col = min (2 * ncols, ncols - ixshift)
+
+ call amovkl (long (1), v, IM_MAXDIM)
+
+ # Shift the image using the appropriate data type operators.
+ switch (IM_PIXTYPE(im1)) {
+ case TY_SHORT:
+ do i = 1, nlines {
+ if (impnls (im2, buf2, v) == EOF)
+ call error (5, wrerr)
+ yline = i - iyshift
+ buf1 = imgs2s (im1, x1col, x2col, yline, yline)
+ if (buf1 == EOF)
+ call error (5, wrerr)
+ call amovs (Mems[buf1], Mems[buf2], ncols)
+ }
+ case TY_INT:
+ do i = 1, nlines {
+ if (impnli (im2, buf2, v) == EOF)
+ call error (5, wrerr)
+ yline = i - iyshift
+ buf1 = imgs2i (im1, x1col, x2col, yline, yline)
+ if (buf1 == EOF)
+ call error (5, wrerr)
+ call amovi (Memi[buf1], Memi[buf2], ncols)
+ }
+ case TY_USHORT, TY_LONG:
+ do i = 1, nlines {
+ if (impnll (im2, buf2, v) == EOF)
+ call error (5, wrerr)
+ yline = i - iyshift
+ buf1 = imgs2l (im1, x1col, x2col, yline, yline)
+ if (buf1 == EOF)
+ call error (5, wrerr)
+ call amovl (Meml[buf1], Meml[buf2], ncols)
+ }
+ case TY_REAL:
+ do i = 1, nlines {
+ if (impnlr (im2, buf2, v) == EOF)
+ call error (5, wrerr)
+ yline = i - iyshift
+ buf1 = imgs2r (im1, x1col, x2col, yline, yline)
+ if (buf1 == EOF)
+ call error (5, wrerr)
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ case TY_DOUBLE:
+ do i = 1, nlines {
+ if (impnld (im2, buf2, v) == EOF)
+ call error (0, wrerr)
+ yline = i - iyshift
+ buf1 = imgs2d (im1, x1col, x2col, yline, yline)
+ if (buf1 == EOF)
+ call error (0, wrerr)
+ call amovd (Memd[buf1], Memd[buf2], ncols)
+ }
+ case TY_COMPLEX:
+ do i = 1, nlines {
+ if (impnlx (im2, buf2, v) == EOF)
+ call error (0, wrerr)
+ yline = i - iyshift
+ buf1 = imgs2x (im1, x1col, x2col, yline, yline)
+ if (buf1 == EOF)
+ call error (0, wrerr)
+ call amovx (Memx[buf1], Memx[buf2], ncols)
+ }
+ default:
+ call error (6, "ISHIFTXY: Unknown IRAF type.")
+ }
+end
+
+
+
+# RG_XFSHIFTIM -- Shift a 1 or 2D image by a fractional pixel amount
+# in x and y.
+
+procedure rg_xfshiftim (im1, im2, xshift, yshift, interpstr, boundary_type,
+ constant)
+
+pointer im1 #I pointer to input image
+pointer im2 #I pointer to output image
+real xshift #I shift in x direction
+real yshift #I shift in y direction
+char interpstr[ARB] #I type of interpolant
+int boundary_type #I type of boundary extension
+real constant #I value of constant for boundary extension
+
+int i, interp_type, nsinc, nincr
+int ncols, nlines, nbpix, fstline, lstline, nxymargin
+int cin1, cin2, nxin, lin1, lin2, nyin
+int lout1, lout2, nyout
+real xshft, yshft, deltax, deltay, dx, dy, cx, ly
+pointer sp, x, y, msi, sinbuf, soutbuf
+bool fp_equalr()
+int msigeti()
+pointer imps2r()
+
+errchk imgs2r, imps2r
+errchk msiinit, msifree, msifit, msigrid
+errchk smark, salloc, sfree
+
+begin
+ ncols = IM_LEN(im1,1)
+ nlines = IM_LEN(im1,2)
+
+ # Check for out of bounds shift.
+ if (xshift < -ncols || xshift > ncols)
+ call error (0, "XC_SHIFTIM: X shift out of bounds.")
+ if (yshift < -nlines || yshift > nlines)
+ call error (0, "XC_SHIFTIM: Y shift out of bounds.")
+
+ # Get the real shift.
+ if (boundary_type == BT_WRAP) {
+ xshft = mod (xshift, real (ncols))
+ yshft = mod (yshift, real (nlines))
+ } else {
+ xshft = xshift
+ yshft = yshift
+ }
+
+ # Allocate temporary space.
+ call smark (sp)
+ call salloc (x, 2 * ncols, TY_REAL)
+ call salloc (y, 2 * nlines, TY_REAL)
+ sinbuf = NULL
+
+ # Define the x and y interpolation coordinates.
+ dx = abs (xshft - int (xshft))
+ if (fp_equalr (dx, 0.0))
+ deltax = 0.0
+ else if (xshft > 0.)
+ deltax = 1. - dx
+ else
+ deltax = dx
+ dy = abs (yshft - int (yshft))
+ if (fp_equalr (dy, 0.0))
+ deltay = 0.0
+ else if (yshft > 0.)
+ deltay = 1. - dy
+ else
+ deltay = dy
+
+ # Initialize the 2-D interpolation routines.
+ call msitype (interpstr, interp_type, nsinc, nincr, cx)
+ if (interp_type == II_BILSINC || interp_type == II_BISINC)
+ call msisinit (msi, interp_type, nsinc, 1, 1,
+ deltax - nint (deltax), deltay - nint (deltay), 0.0)
+ else
+ call msisinit (msi, interp_type, nsinc, 1, 1, cx, cx, 0.0)
+
+ # Set boundary extension parameters.
+ if (interp_type == II_BISPLINE3)
+ nxymargin = NMARGIN_SPLINE3
+ else if (interp_type == II_BISINC || interp_type == II_BILSINC)
+ nxymargin = msigeti (msi, II_MSINSINC)
+ else
+ nxymargin = NMARGIN
+ nbpix = max (int (abs(xshft)+1.0), int (abs(yshft)+1.0)) + nxymargin
+ call imseti (im1, IM_NBNDRYPIX, nbpix)
+ call imseti (im1, IM_TYBNDRY, boundary_type)
+ if (boundary_type == BT_CONSTANT)
+ call imsetr (im1, IM_BNDRYPIXVAL, constant)
+
+ # Define the x interpolation coordinates.
+ deltax = deltax + nxymargin
+ if (interp_type == II_BIDRIZZLE) {
+ do i = 1, ncols {
+ Memr[x+2*i-2] = i + deltax - 0.5
+ Memr[x+2*i-1] = i + deltax + 0.5
+ }
+ } else {
+ do i = 1, ncols
+ Memr[x+i-1] = i + deltax
+ }
+
+ # Define the y interpolation coordinates.
+ deltay = deltay + nxymargin
+ if (interp_type == II_BIDRIZZLE) {
+ do i = 1, NYOUT {
+ Memr[y+2*i-2] = i + deltay - 0.5
+ Memr[y+2*i-1] = i + deltay + 0.5
+ }
+ } else {
+ do i = 1, NYOUT
+ Memr[y+i-1] = i + deltay
+ }
+
+ # Define column range in the input image.
+ cx = 1. - nxymargin - xshft
+ if ((cx <= 0.) && (! fp_equalr (dx, 0.0)))
+ cin1 = int (cx) - 1
+ else
+ cin1 = int (cx)
+ cin2 = ncols - xshft + nxymargin + 1
+ nxin = cin2 - cin1 + 1
+
+ # Loop over output sections.
+ for (lout1 = 1; lout1 <= nlines; lout1 = lout1 + NYOUT) {
+
+ # Define range of output lines.
+ lout2 = min (lout1 + NYOUT - 1, nlines)
+ nyout = lout2 - lout1 + 1
+
+ # Define correspoding range of input lines.
+ ly = lout1 - nxymargin - yshft
+ if ((ly <= 0) && (! fp_equalr (dy, 0.0)))
+ lin1 = int (ly) - 1
+ else
+ lin1 = int (ly)
+ lin2 = lout2 - yshft + nxymargin + 1
+ nyin = lin2 - lin1 + 1
+
+ # Get appropriate input image section and compute the coefficients.
+ if ((sinbuf == NULL) || (lin1 < fstline) || (lin2 > lstline)) {
+ fstline = lin1
+ lstline = lin2
+ call rg_buf (im1, cin1, cin2, lin1, lin2, sinbuf)
+ call msifit (msi, Memr[sinbuf], nxin, nyin, nxin)
+ }
+
+ # Output the image section.
+ soutbuf = imps2r (im2, 1, ncols, lout1, lout2)
+ if (soutbuf == EOF)
+ call error (0, "GSHIFTXY: Error writing output image.")
+
+ # Evaluate the interpolant.
+ call msigrid (msi, Memr[x], Memr[y], Memr[soutbuf], ncols, nyout,
+ ncols)
+ }
+
+ call msifree (msi)
+ call sfree (sp)
+end
+
+
+# RG_BUF -- Procedure to provide a buffer of image lines with minimum reads
+
+procedure rg_buf (im, col1, col2, line1, line2, buf)
+
+pointer im #I pointer to input image
+int col1, col2 #I column range of input buffer
+int line1, line2 #I line range of input buffer
+pointer buf #I buffer
+
+int i, ncols, nlines, nclast, llast1, llast2, nllast
+pointer buf1, buf2
+
+pointer imgs2r()
+
+begin
+ ncols = col2 - col1 + 1
+ nlines = line2 - line1 + 1
+
+ if (buf == NULL) {
+ call malloc (buf, ncols * nlines, TY_REAL)
+ llast1 = line1 - nlines
+ llast2 = line2 - nlines
+ } else if ((nlines != nllast) || (ncols != nclast)) {
+ call realloc (buf, ncols * nlines, TY_REAL)
+ llast1 = line1 - nlines
+ llast2 = line2 - nlines
+ }
+
+ if (line1 < llast1) {
+ do i = line2, line1, -1 {
+ if (i > llast1)
+ buf1 = buf + (i - llast1) * ncols
+ else
+ buf1 = imgs2r (im, col1, col2, i, i)
+ buf2 = buf + (i - line1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ } else if (line2 > llast2) {
+ do i = line1, line2 {
+ if (i < llast2)
+ buf1 = buf + (i - llast1) * ncols
+ else
+ buf1 = imgs2r (im, col1, col2, i, i)
+ buf2 = buf + (i - line1) * ncols
+ call amovr (Memr[buf1], Memr[buf2], ncols)
+ }
+ }
+
+ llast1 = line1
+ llast2 = line2
+ nclast = ncols
+ nllast = nlines
+end
diff --git a/pkg/images/immatch/src/xregister/rgxplot.x b/pkg/images/immatch/src/xregister/rgxplot.x
new file mode 100644
index 00000000..8b347ab5
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxplot.x
@@ -0,0 +1,317 @@
+include <imhdr.h>
+include <gset.h>
+
+# RG_XPLINE -- Plot a line of reference and input image.
+
+procedure rg_xpline (gd, imr, im, nliner, xshift, yshift)
+
+pointer gd #I pointer to the graphics stream
+pointer imr #I pointer to the reference image
+pointer im #I pointer to the image
+int nliner #I the reference line
+int xshift #I x shift
+int yshift #I y shift
+
+int i, rncols, rnlines, incols, inlines
+pointer sp, title, xr, xi, ptrr, ptri
+real ymin, ymax, tymin, tymax
+int strlen()
+pointer imgl1r(), imgl2r()
+
+begin
+ # Return if no graphics stream.
+ if (gd == NULL)
+ return
+
+ # Check for valid line number.
+ rncols = IM_LEN(imr,1)
+ if (IM_NDIM(imr) == 1)
+ rnlines = 1
+ else
+ rnlines = IM_LEN(imr,2)
+ incols = IM_LEN(im,1)
+ if (IM_NDIM(im) == 1)
+ inlines = 1
+ else
+ inlines = IM_LEN(im,2)
+ if ((nliner < 1) || (nliner > rnlines))
+ return
+ if (((nliner + yshift) < 1) || ((nliner + yshift) > inlines))
+ return
+
+ # Allocate working space.
+ call smark (sp)
+ call salloc (title, SZ_LINE, TY_CHAR)
+ call salloc (xr, rncols, TY_REAL)
+ call salloc (xi, rncols, TY_REAL)
+
+ # Initialize the x data data.
+ do i = 1, rncols {
+ Memr[xr+i-1] = i
+ Memr[xi+i-1] = i - xshift
+ }
+
+ # Initalize the y data.
+ if (IM_NDIM(imr) == 1)
+ ptrr = imgl1r (imr)
+ else
+ ptrr = imgl2r (imr, nliner)
+ if (IM_NDIM(im) == 1)
+ ptri = imgl1r (im)
+ else
+ ptri = imgl2r (im, nliner + yshift)
+ call alimr (Memr[ptrr], rncols, ymin, ymax)
+ call alimr (Memr[ptri], incols, tymin, tymax)
+ ymin = min (ymin, tymin)
+ ymax = max (ymax, tymax)
+
+ # Construct the title.
+ call sprintf (Memc[title], SZ_LINE,
+ "Refimage: %s Image: %s\n")
+ call pargstr (IM_HDRFILE(imr))
+ call pargstr (IM_HDRFILE(im))
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "Refline (solid): %d Inline (dashed): %d Xlag: %d Ylag: %d")
+ call pargi (nliner)
+ call pargi (nliner + yshift)
+ call pargi (xshift)
+ call pargi (yshift)
+
+ # Set up the axes labels and window.
+ call gclear (gd)
+ call gswind (gd, 1.0, real(rncols), ymin, ymax)
+ call glabax (gd, Memc[title], "Column Number", "Counts")
+
+ # Plot the two lines.
+ call gseti (gd, G_PLTYPE, GL_SOLID)
+ call gpline (gd, Memr[xr], Memr[ptrr], rncols)
+ call gseti (gd, G_PLTYPE, GL_DASHED)
+ call gpline (gd, Memr[xi], Memr[ptri], incols)
+ call gflush (gd)
+
+ call sfree (sp)
+end
+
+
+# RG_XPCOL -- Plot a column in the reference and input image.
+
+procedure rg_xpcol (gd, imr, im, ncolr, xshift, yshift)
+
+pointer gd #I pointer to the graphics stream
+pointer imr #I pointer to the reference image
+pointer im #I pointer to the image
+int ncolr #I the line number
+int xshift #I xshift to be applied
+int yshift #I yshift to be applied
+
+int i, rncols, rnlines, incols, inlines
+pointer sp, title, xr, xi, ptrr, ptri
+real ymin, ymax, tymin, tymax
+int strlen()
+pointer imgs1r(), imgs2r()
+
+begin
+ # Return if no graphics stream.
+ if (gd == NULL)
+ return
+
+ # Check for valid column number.
+ rncols = IM_LEN(imr,1)
+ if (IM_NDIM(imr) == 1)
+ rnlines = 1
+ else
+ rnlines = IM_LEN(imr,2)
+ incols = IM_LEN(im,1)
+ if (IM_NDIM(im) == 1)
+ inlines = 1
+ else
+ inlines = IM_LEN(im,2)
+ if ((ncolr < 1) || (ncolr > rncols))
+ return
+ if (((ncolr - xshift) < 1) || ((ncolr - xshift) > incols))
+ return
+
+ # Allocate valid working space.
+ call smark (sp)
+ call salloc (title, SZ_LINE, TY_CHAR)
+ call salloc (xr, rnlines, TY_REAL)
+ call salloc (xi, inlines, TY_REAL)
+
+ # Initialize the data.
+ do i = 1, rnlines {
+ Memr[xr+i-1] = i
+ Memr[xi+i-1] = i - yshift
+ }
+ if (IM_NDIM(imr) == 1)
+ ptrr = imgs1r (imr, ncolr, ncolr)
+ else
+ ptrr = imgs2r (imr, ncolr, ncolr, 1, rnlines)
+ if (IM_NDIM(im) == 1)
+ ptri = imgs1r (im, ncolr + xshift, ncolr + xshift)
+ else
+ ptri = imgs2r (im, ncolr + xshift, ncolr + xshift, 1, inlines)
+ call alimr (Memr[ptrr], rnlines, ymin, ymax)
+ call alimr (Memr[ptri], inlines, tymin, tymax)
+ ymin = min (ymin, tymin)
+ ymax = max (ymax, tymax)
+
+ # Construct the title.
+ call sprintf (Memc[title], SZ_LINE, "Refimage: %s Image: %s\n")
+ call pargstr (IM_HDRFILE(imr))
+ call pargstr (IM_HDRFILE(im))
+ call sprintf (Memc[title+strlen(Memc[title])], SZ_LINE,
+ "Refcol (solid): %d Imcol (dashed): %d Xlag: %d Ylag: %d")
+ call pargi (ncolr)
+ call pargi (ncolr + xshift)
+ call pargi (xshift)
+ call pargi (yshift)
+
+ # Set up the labels and the axes.
+ call gclear (gd)
+ call gswind (gd, 1.0, real (rnlines), ymin, ymax)
+ call glabax (gd, Memc[title], "Line Number", "Counts")
+
+ # Plot the profile.
+ call gseti (gd, G_PLTYPE, GL_SOLID)
+ call gpline (gd, Memr[xr], Memr[ptrr], rnlines)
+ call gseti (gd, G_PLTYPE, GL_DASHED)
+ call gpline (gd, Memr[xi], Memr[ptri], rnlines)
+ call gflush (gd)
+
+ call sfree (sp)
+end
+
+
+# RG_XCPLINE -- Plot a line of the 2D correlation function.
+
+procedure rg_xcpline (gd, title, data, nx, ny, nline)
+
+pointer gd #I pointer to the graphics stream
+char title[ARB] #I title for the plot
+real data[nx,ARB] #I the input data array
+int nx, ny #I dimensions of the input data array
+int nline #I the line number
+
+int i
+pointer sp, str, x
+real ymin, ymax
+
+begin
+ # Return if no graphics stream.
+ if (gd == NULL)
+ return
+
+ # Check for valid line number.
+ if (nline < 1 || nline > ny)
+ return
+
+ # Allocate some working space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+ call salloc (x, nx, TY_REAL)
+
+ # Initialize the data.
+ do i = 1, nx
+ Memr[x+i-1] = i
+ call alimr (data[1,nline], nx, ymin, ymax)
+
+ # Set up the labels and the axes.
+ call gclear (gd)
+ call gswind (gd, 1.0, real (nx), ymin, ymax)
+ call glabax (gd, title, "X Lag", "X-Correlation Function")
+
+ # Plot the line profile.
+ call gseti (gd, G_PLTYPE, GL_SOLID)
+ call gpline (gd, Memr[x], data[1,nline], nx)
+ call gflush (gd)
+
+ call sfree (sp)
+end
+
+
+# RG_XCPCOL -- Plot a column of the cross-correlation function.
+
+procedure rg_xcpcol (gd, title, data, nx, ny, ncol)
+
+pointer gd #I pointer to the graphics stream
+char title[ARB] #I title of the column plot
+real data[nx,ARB] #I the input data array
+int nx, ny #I the dimensions of the input data array
+int ncol #I line number
+
+int i
+pointer sp, x, y
+real ymin, ymax
+
+begin
+ # Return if no graphics stream.
+ if (gd == NULL)
+ return
+
+ # Check for valid column number.
+ if (ncol < 1 || ncol > nx)
+ return
+
+ # Initialize.
+ call smark (sp)
+ call salloc (x, ny, TY_REAL)
+ call salloc (y, ny, TY_REAL)
+
+ # Get the data to be plotted.
+ do i = 1, ny {
+ Memr[x+i-1] = i
+ Memr[y+i-1] = data[ncol,i]
+ }
+ call alimr (Memr[y], ny, ymin, ymax)
+
+ # Set up the labels and the axes.
+ call gclear (gd)
+ call gswind (gd, 1.0, real (ny), ymin, ymax)
+ call glabax (gd, title, "Y Lag", "X-Correlation Function")
+
+ # Plot the profile.
+ call gseti (gd, G_PLTYPE, GL_SOLID)
+ call gpline (gd, Memr[x], Memr[y], ny)
+ call gflush (gd)
+
+ call sfree (sp)
+end
+
+
+# RG_XMKPEAK -- Procedure to mark the peak from a correlation function
+# contour plot.
+
+procedure rg_xmkpeak (gd, xwindow, ywindow, xshift, yshift)
+
+pointer gd #I pointer to the graphics stream
+int xwindow #I x dimension of correlation function
+int ywindow #I y dimension of correlation function
+real xshift #O x shift
+real yshift #O y shift
+
+int wcs, key
+pointer sp, cmd
+real wx, wy
+int clgcur()
+
+begin
+ if (gd == NULL)
+ return
+
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ call printf ("Mark peak of the cross correlation function\n")
+ if (clgcur ("gcommands", wx, wy, wcs, key, Memc[cmd], SZ_LINE) == EOF)
+ ;
+ if (wx < 1.0 || wx > real (xwindow) || wy < 1.0 || wy >
+ real (ywindow)) {
+ xshift = 0.0
+ yshift = 0.0
+ } else {
+ xshift = wx - (1 + xwindow) / 2
+ yshift = wy - (1 + ywindow) / 2
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/xregister/rgxppars.x b/pkg/images/immatch/src/xregister/rgxppars.x
new file mode 100644
index 00000000..2dc6aafd
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxppars.x
@@ -0,0 +1,49 @@
+include "xregister.h"
+
+# RG_PXPARS -- Update the cross-correlation algorithm parameters.
+
+procedure rg_pxpars (xc)
+
+pointer xc #I pointer to the cross-correlation structure
+
+pointer sp, str
+int rg_xstati()
+real rg_xstatr()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Define the regions.
+ call rg_xstats (xc, REGIONS, Memc[str], SZ_LINE)
+ call clpstr ("regions", Memc[str])
+ call clputi ("xlag", rg_xstati (xc, XLAG))
+ call clputi ("ylag", rg_xstati (xc, YLAG))
+ call clputi ("dxlag", rg_xstati (xc, DXLAG))
+ call clputi ("dylag", rg_xstati (xc, DYLAG))
+
+ # Store the background fitting parameters.
+ call rg_xstats (xc, BSTRING, Memc[str], SZ_LINE)
+ call clpstr ("background", Memc[str])
+ call clputi ("border", rg_xstati (xc, BORDER))
+ call clputr ("loreject", rg_xstatr (xc, LOREJECT))
+ call clputr ("hireject", rg_xstatr (xc, HIREJECT))
+ call clputr ("apodize", rg_xstatr (xc, APODIZE))
+ call rg_xstats (xc, FSTRING, Memc[str], SZ_LINE)
+ call clpstr ("filter", Memc[str])
+
+ # Store the cross-correlation parameters.
+ call rg_xstats (xc, CSTRING, Memc[str], SZ_LINE)
+ call clpstr ("correlation", Memc[str])
+ call clputi ("xwindow", rg_xstati (xc, XWINDOW))
+ call clputi ("ywindow", rg_xstati (xc, YWINDOW))
+
+ # Store the peak centering parameters.
+ call rg_xstats (xc, PSTRING, Memc[str], SZ_LINE)
+ call clpstr ("function", Memc[str])
+ call clputi ("xcbox", rg_xstati (xc, XCBOX))
+ call clputi ("ycbox", rg_xstati (xc, YCBOX))
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/xregister/rgxregions.x b/pkg/images/immatch/src/xregister/rgxregions.x
new file mode 100644
index 00000000..ed682f61
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxregions.x
@@ -0,0 +1,459 @@
+include <fset.h>
+include <ctype.h>
+include <imhdr.h>
+include "xregister.h"
+
+# RG_XREGIONS -- Decode the image sections into regions. If the sections string
+# is NULL then the regions list is initially empty and depending on the mode
+# of the task, XREGISTER will or will not complain.Otherwise the image
+# sections specified in the sections string or file are decoded into a
+# regions list.
+
+int procedure rg_xregions (list, im, xc, rp)
+
+int list #I pointer to the regions list
+pointer im #I pointer to the reference image
+pointer xc #I pointer to the cross-correlation structure
+int rp #I index of the current region
+
+int fd, nregions
+pointer sp, fname, regions
+int rg_xgrid(), rg_xgregions(), rg_xrregions(), rg_xstati(), fntgfnb()
+int open()
+errchk fntgfnb(), open(), close()
+
+begin
+ call smark (sp)
+ call salloc (fname, SZ_FNAME, TY_CHAR)
+ call salloc (regions, SZ_LINE, TY_CHAR)
+
+ call rg_xstats (xc, REGIONS, Memc[regions], SZ_LINE)
+ if (rp < 1 || rp > MAX_NREGIONS || Memc[regions] == EOS) {
+ nregions = 0
+ } else if (rg_xgrid (im, xc, rp, MAX_NREGIONS) > 0) {
+ nregions = rg_xstati (xc, NREGIONS)
+ } else if (rg_xgregions (im, xc, rp, MAX_NREGIONS) > 0) {
+ nregions = rg_xstati (xc, NREGIONS)
+ } else if (list != NULL) {
+ iferr {
+ if (fntgfnb (list, Memc[fname], SZ_FNAME) != EOF) {
+ fd = open (Memc[fname], READ_ONLY, TEXT_FILE)
+ nregions= rg_xrregions (fd, im, xc, rp, MAX_NREGIONS)
+ call close (fd)
+ }
+ } then
+ nregions = 0
+ } else
+ nregions = 0
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_XMKREGIONS -- Create a list of regions by marking image sections
+# on the image display.
+
+int procedure rg_xmkregions (im, xc, rp, max_nregions, regions, maxch)
+
+pointer im #I pointer to the reference image
+pointer xc #I pointer to the cross-correlation structure
+int rp #I index of the current region
+int max_nregions #I the maximum number of regions
+char regions[ARB] #O the output regions string
+int maxch #I maximum size of the output regions string
+
+int op, nregions, wcs, key
+pointer sp, region, section, cmd
+real xll, yll, xur, yur
+int rg_xstati(), clgcur(), gstrcpy()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_LINE, TY_CHAR)
+ call salloc (section, SZ_LINE, TY_CHAR)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_xrealloc (xc, max_nregions)
+
+ # Initialize.
+ nregions = min (rp-1, rg_xstati (xc, NREGIONS))
+ op = 1
+
+ # Mark the sections on the display.
+ while (nregions < max_nregions) {
+
+ call printf ("Mark lower left corner of region %d [q to quit].\n")
+ call pargi (nregions + 1)
+ if (clgcur ("icommands", xll, yll, wcs, key, Memc[cmd],
+ SZ_LINE) == EOF)
+ break
+ if (key == 'q')
+ break
+
+ call printf ("Mark upper right corner of region %d [q to quit].\n")
+ call pargi (nregions + 1)
+ if (clgcur ("icommands", xur, yur, wcs, key, Memc[cmd],
+ SZ_LINE) == EOF)
+ break
+ if (key == 'q')
+ break
+
+ if (xll < 1.0 || xur > IM_LEN(im,1) || yll < 1.0 || yur >
+ IM_LEN(im,2))
+ break
+
+ Memi[rg_xstatp(xc,RC1)+nregions] = nint (xll)
+ Memi[rg_xstatp(xc,RC2)+nregions] = nint (xur)
+ Memi[rg_xstatp(xc,RL1)+nregions] = nint (yll)
+ Memi[rg_xstatp(xc,RL2)+nregions] = nint (yur)
+ Memr[rg_xstatp(xc,RZERO)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RXSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RYSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,XSHIFTS)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,YSHIFTS)+nregions] = INDEFR
+ nregions = nregions + 1
+
+ # Write the first 9 regions into the regions string.
+ call sprintf (Memc[cmd], SZ_LINE, "[%d:%d,%d:%d] ")
+ call pargi (nint (xll))
+ call pargi (nint (xur))
+ call pargi (nint (yll))
+ call pargi (nint (yur))
+ op = op + gstrcpy (Memc[cmd], regions[op], maxch - op + 1)
+ }
+ call printf ("\n")
+
+ # Reallocate the correct amount of space.
+ call rg_xseti (xc, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_xrealloc (xc, nregions)
+ else
+ call rg_xrfree (xc)
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_XGRID - Decode the regions from a grid specification.
+
+int procedure rg_xgrid (im, xc, rp, max_nregions)
+
+pointer im #I pointer to the reference image
+pointer xc #I pointer to the cross-correlation structure
+int rp #I index of the current region
+int max_nregions #I the maximum number of regions
+
+int i, istart, iend, j, jstart, jend, ncols, nlines, nxsample, nysample
+int nxcols, nylines, nregions
+pointer sp, region, section
+int rg_xstati(), nscan(), strcmp()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_LINE, TY_CHAR)
+ call salloc (section, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_xrealloc (xc, max_nregions)
+
+ # Initialize.
+ call rg_xstats (xc, REGIONS, Memc[region], SZ_LINE)
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+ nregions = min (rp - 1, rg_xstati (xc, NREGIONS))
+
+ # Decode the grid specification.
+ call sscan (Memc[region])
+ call gargwrd (Memc[section], SZ_LINE)
+ call gargi (nxsample)
+ call gargi (nysample)
+ if ((nscan() != 3) || (strcmp (Memc[section], "grid") != 0)) {
+ call sfree (sp)
+ return (nregions)
+ }
+
+ # Decode the regions.
+ if ((nxsample * nysample) > max_nregions) {
+ nxsample = nint (sqrt (real (max_nregions) * real (ncols) /
+ real (nlines)))
+ nysample = real (max_nregions) / real (nxsample)
+ }
+ nxcols = ncols / nxsample
+ nylines = nlines / nysample
+ jstart = 1 + (nlines - nysample * nylines) / 2
+ jend = jstart + (nysample - 1) * nylines
+ do j = jstart, jend, nylines {
+ istart = 1 + (ncols - nxsample * nxcols) / 2
+ iend = istart + (nxsample - 1) * nxcols
+ do i = istart, iend, nxcols {
+ Memi[rg_xstatp(xc,RC1)+nregions] = i
+ Memi[rg_xstatp(xc,RC2)+nregions] = i + nxcols - 1
+ Memi[rg_xstatp(xc,RL1)+nregions] = j
+ Memi[rg_xstatp(xc,RL2)+nregions] = j + nylines - 1
+ Memr[rg_xstatp(xc,RZERO)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RXSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RYSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,XSHIFTS)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,YSHIFTS)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+ }
+
+ call rg_xseti (xc, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_xrealloc (xc, nregions)
+ else
+ call rg_xrfree (xc)
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_XRREGIONS -- Read and decode the regions from a file.
+
+int procedure rg_xrregions (fd, im, xc, rp, max_nregions)
+
+int fd #I regions file descriptor
+pointer im #I pointer to the reference image
+pointer xc #I pointer to the cross-correlation structure
+int rp #I index of the current region
+int max_nregions #I the maximum number of regions
+
+int ncols, nlines, nregions, x1, y1, x2, y2, step
+pointer sp, line, section
+int rg_xstati(), getline(), rg_xgsections()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (line, SZ_LINE, TY_CHAR)
+ call salloc (section, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information,
+ call rg_xrealloc (xc, max_nregions)
+
+ # Initialize.
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+ nregions = min (rp - 1, rg_xstati (xc, NREGIONS))
+
+ # Decode the regions string.
+ while ((getline (fd, Memc[line]) != EOF) && nregions < max_nregions) {
+ call sscan (Memc[line])
+ call gargwrd (Memc[section], SZ_LINE)
+ while ((Memc[section] != EOS) && (nregions < max_nregions)) {
+ if (rg_xgsections (Memc[section], x1, x2, step, y1, y2, step,
+ ncols, nlines) == OK) {
+ Memi[rg_xstatp(xc,RC1)+nregions] = x1
+ Memi[rg_xstatp(xc,RC2)+nregions] = x2
+ Memi[rg_xstatp(xc,RL1)+nregions] = y1
+ Memi[rg_xstatp(xc,RL2)+nregions] = y2
+ Memr[rg_xstatp(xc,RZERO)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RXSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RYSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,XSHIFTS)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,YSHIFTS)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+ call gargwrd (Memc[section], SZ_LINE)
+ }
+ }
+
+ # Reallocate the correct amount of space.
+ call rg_xseti (xc, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_xrealloc (xc, nregions)
+ else
+ call rg_xrfree (xc)
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_XGREGIONS -- Decode a list of regions from a string containing
+# a list of sections.
+
+int procedure rg_xgregions (im, xc, rp, max_nregions)
+
+pointer im #I pointer to the reference image
+pointer xc #I pointer to cross-correlation structure
+int rp #I the index of the current region
+int max_nregions #I the maximum number of regions
+
+int ncols, nlines, nregions, x1, x2, y1, y2, step
+pointer sp, section, region
+int rg_xstati(), rg_xgsections()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (region, SZ_LINE, TY_CHAR)
+ call salloc (section, SZ_LINE, TY_CHAR)
+
+ # Allocate the arrays to hold the regions information.
+ call rg_xrealloc (xc, max_nregions)
+
+ # Initialize.
+ call rg_xstats (xc, REGIONS, Memc[region], SZ_LINE)
+ ncols = IM_LEN(im,1)
+ nlines = IM_LEN(im,2)
+ nregions = min (rp - 1, rg_xstati (xc, NREGIONS))
+
+ # Decode the sections
+ call sscan (Memc[region])
+ call gargwrd (Memc[section], SZ_LINE)
+ while ((Memc[section] != EOS) && (nregions < max_nregions)) {
+ if (rg_xgsections (Memc[section], x1, x2, step, y1, y2, step,
+ ncols, nlines) == OK) {
+ Memi[rg_xstatp(xc,RC1)+nregions] = x1
+ Memi[rg_xstatp(xc,RC2)+nregions] = x2
+ Memi[rg_xstatp(xc,RL1)+nregions] = y1
+ Memi[rg_xstatp(xc,RL2)+nregions] = y2
+ Memr[rg_xstatp(xc,RZERO)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RXSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,RYSLOPE)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,XSHIFTS)+nregions] = INDEFR
+ Memr[rg_xstatp(xc,YSHIFTS)+nregions] = INDEFR
+ nregions = nregions + 1
+ }
+ call gargwrd (Memc[section], SZ_LINE)
+ }
+
+
+ # Reallocate the correct amount of space.
+ call rg_xseti (xc, NREGIONS, nregions)
+ if (nregions > 0)
+ call rg_xrealloc (xc, nregions)
+ else
+ call rg_xrfree (xc)
+
+ call sfree (sp)
+
+ return (nregions)
+end
+
+
+# RG_XGSECTIONS -- Decode an image section into column and line limits
+# and a step size. Sections which describe the whole image are decoded into
+# a block ncols * nlines long.
+
+int procedure rg_xgsections (section, x1, x2, xstep, y1, y2, ystep, ncols,
+ nlines)
+
+char section[ARB] #I the input section string
+int x1, x2 #O the output column section limits
+int xstep #O the output column step size
+int y1, y2 #O the output line section limits
+int ystep #O the output line step size
+int ncols, nlines #I the maximum number of lines and columns
+
+int ip
+int rg_xgdim()
+
+begin
+ ip = 1
+ if (rg_xgdim (section, ip, x1, x2, xstep, ncols) == ERR)
+ return (ERR)
+ if (rg_xgdim (section, ip, y1, y2, ystep, nlines) == ERR)
+ return (ERR)
+
+ return (OK)
+end
+
+
+# RG_XGDIM -- Decode a single subscript expression to produce the
+# range of values for that subscript (X1:X2), and the sampling step size, STEP.
+# Note that X1 may be less than, greater than, or equal to X2, and STEP may
+# be a positive or negative nonzero integer. Various shorthand notations are
+# permitted, as is embedded whitespace.
+
+int procedure rg_xgdim (section, ip, x1, x2, step, limit)
+
+char section[ARB] #I the input image section
+int ip #I/O pointer to the position in section string
+int x1 #O first limit of dimension
+int x2 #O second limit of dimension
+int step #O step size of dimension
+int limit #I maximum size of dimension
+
+int temp
+int ctoi()
+
+begin
+ x1 = 1
+ x2 = limit
+ step = 1
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ if (section[ip] =='[')
+ ip = ip + 1
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ # Get X1, X2.
+ if (ctoi (section, ip, temp) > 0) { # [x1
+ x1 = max (1, min (temp, limit))
+ if (section[ip] == ':') {
+ ip = ip + 1
+ if (ctoi (section, ip, temp) == 0) # [x1:x2
+ return (ERR)
+ x2 = max (1, min (temp, limit))
+ } else
+ x2 = x1
+
+ } else if (section[ip] == '-') {
+ x1 = limit
+ x2 = 1
+ ip = ip + 1
+ if (section[ip] == '*')
+ ip = ip + 1
+
+ } else if (section[ip] == '*') # [*
+ ip = ip + 1
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ # Get sample step size, if give.
+ if (section[ip] == ':') { # ..:step
+ ip = ip + 1
+ if (ctoi (section, ip, step) == 0)
+ return (ERR)
+ else if (step == 0)
+ return (ERR)
+ }
+
+ # Allow notation such as "-*:5", (or even "-:5") where the step
+ # is obviously supposed to be negative.
+
+ if (x1 > x2 && step > 0)
+ step = -step
+
+ while (IS_WHITE(section[ip]))
+ ip = ip + 1
+
+ if (section[ip] == ',') {
+ ip = ip + 1
+ return (OK)
+ } else if (section[ip] == ']')
+ return (OK)
+ else
+ return (ERR)
+end
diff --git a/pkg/images/immatch/src/xregister/rgxshow.x b/pkg/images/immatch/src/xregister/rgxshow.x
new file mode 100644
index 00000000..3a746d9c
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxshow.x
@@ -0,0 +1,172 @@
+include "xregister.h"
+
+# RG_XSHOW -- Show the XREGISTER parameters.
+
+procedure rg_xshow (xc)
+
+pointer xc #I pointer to the main xregister structure
+
+begin
+ call rg_xnshow (xc)
+ call printf ("\n")
+ call rg_xbshow (xc)
+ call printf ("\n")
+ call rg_xxshow (xc)
+ call printf ("\n")
+ call rg_xpshow (xc)
+end
+
+
+# RG_XNSHOW -- Show the input/output data XREGISTER parameters.
+
+procedure rg_xnshow (xc)
+
+pointer xc #I pointer to the main xregister structure
+
+pointer sp, str
+int rg_xstati()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Set the object characteristics.
+ call printf ("\nInput/output data\n")
+ call rg_xstats (xc, IMAGE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_IMAGE)
+ call pargstr (Memc[str])
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_REFIMAGE)
+ call pargstr (Memc[str])
+ call rg_xstats (xc, REGIONS, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_REGIONS)
+ call pargstr (Memc[str])
+ call printf (" %s = %d %s = %d\n")
+ call pargstr (KY_XLAG)
+ call pargi (rg_xstati (xc, XLAG))
+ call pargstr (KY_YLAG)
+ call pargi (rg_xstati (xc, YLAG))
+ call printf (" %s = %d %s = %d\n")
+ call pargstr (KY_DXLAG)
+ call pargi (rg_xstati (xc, DXLAG))
+ call pargstr (KY_DYLAG)
+ call pargi (rg_xstati (xc, DYLAG))
+ call rg_xstats (xc, DATABASE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_DATABASE)
+ call pargstr (Memc[str])
+ call rg_xstats (xc, RECORD, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_RECORD)
+ call pargstr (Memc[str])
+ call rg_xstats (xc, REFFILE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_REFFILE)
+ call pargstr (Memc[str])
+ call rg_xstats (xc, OUTIMAGE, Memc[str], SZ_FNAME)
+ call printf (" %s: %s\n")
+ call pargstr (KY_OUTIMAGE)
+ call pargstr (Memc[str])
+
+ call sfree (sp)
+end
+
+
+# RG_XBSHOW -- Show the background fitting parameters.
+
+procedure rg_xbshow (xc)
+
+pointer xc #I pointer to the main xregister structure
+
+int back
+pointer sp, str
+int rg_xstati()
+real rg_xstatr()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ back = rg_xstati (xc, BACKGRD)
+ call printf ("Background fitting parameters:\n")
+ call rg_xstats (xc, BSTRING, Memc[str], SZ_LINE)
+ call printf (" %s: %s\n")
+ call pargstr (KY_BACKGROUND)
+ call pargstr (Memc[str])
+ call printf (" %s = %d\n")
+ call pargstr (KY_BORDER)
+ call pargi (rg_xstati (xc, BORDER))
+ call printf (" %s = %g %s = %g\n")
+ call pargstr (KY_LOREJECT)
+ call pargr (rg_xstatr (xc, LOREJECT))
+ call pargstr (KY_HIREJECT)
+ call pargr (rg_xstatr (xc, HIREJECT))
+ call printf (" %s = %g\n")
+ call pargstr (KY_APODIZE)
+ call pargr (rg_xstatr (xc, APODIZE))
+ call rg_xstats (xc, FSTRING, Memc[str], SZ_LINE)
+ call printf (" %s: %s\n")
+ call pargstr (KY_FILTER)
+ call pargstr (Memc[str])
+
+ call sfree (sp)
+end
+
+
+# RG_XXSHOW -- Show the cross-correlation function parameters.
+
+procedure rg_xxshow (xc)
+
+pointer xc #I pointer to the main xregister structure
+
+pointer sp, str
+int rg_xstati()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ call printf ("Cross correlation function:\n")
+ call rg_xstats (xc, CSTRING, Memc[str], SZ_LINE)
+ call printf (" %s: %s\n")
+ call pargstr (KY_CORRELATION)
+ call pargstr (Memc[str])
+ call printf (" %s = %d %s = %d\n")
+ call pargstr (KY_XWINDOW)
+ call pargi (rg_xstati (xc, XWINDOW))
+ call pargstr (KY_YWINDOW)
+ call pargi (rg_xstati (xc, YWINDOW))
+
+ call sfree (sp)
+end
+
+
+# RG_XPSHOW -- Show the peak centering parameters.
+
+procedure rg_xpshow (xc)
+
+pointer xc #I pointer to the main xregister structure
+
+pointer sp, str
+int rg_xstati()
+
+begin
+ call smark (sp)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ call printf ("Peak centering parameters:\n")
+ call rg_xstats (xc, PSTRING, Memc[str], SZ_LINE)
+ call printf (" %s: %s\n")
+ call pargstr (KY_PEAKCENTER)
+ call pargstr (Memc[str])
+ call printf (" %s = %d %s = %d\n")
+ call pargstr (KY_XCBOX)
+ call pargi (rg_xstati (xc, XCBOX))
+ call pargstr (KY_YCBOX)
+ call pargi (rg_xstati (xc, YCBOX))
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/xregister/rgxtools.x b/pkg/images/immatch/src/xregister/rgxtools.x
new file mode 100644
index 00000000..e1fb921e
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxtools.x
@@ -0,0 +1,685 @@
+include "xregister.h"
+
+# RG_XINIT -- Initialize the cross-correlation code fitting structure.
+
+procedure rg_xinit (xc, cfunc)
+
+pointer xc #O pointer to the cross-correlation structure
+int cfunc #I the input cross-correlation function
+
+begin
+ call malloc (xc, LEN_XCSTRUCT, TY_STRUCT)
+
+ # Initialize the regions pointers.
+ XC_RC1(xc) = NULL
+ XC_RC2(xc) = NULL
+ XC_RL1(xc) = NULL
+ XC_RL2(xc) = NULL
+ XC_RZERO(xc) = NULL
+ XC_RXSLOPE(xc) = NULL
+ XC_RYSLOPE(xc) = NULL
+ XC_XSHIFTS(xc) = NULL
+ XC_YSHIFTS(xc) = NULL
+ XC_TXSHIFT(xc) = 0.0
+ XC_TYSHIFT(xc) = 0.0
+ XC_NREGIONS(xc) = 0
+ XC_CREGION(xc) = 1
+
+ # Set up transformation parameters.
+ XC_NREFPTS(xc) = 0
+ call malloc (XC_XREF(xc), MAX_NREF, TY_REAL)
+ call malloc (XC_YREF(xc), MAX_NREF, TY_REAL)
+ call malloc (XC_TRANSFORM(xc), MAX_NTRANSFORM, TY_REAL)
+
+ # Initialize the region offsets
+ XC_IXLAG(xc) = DEF_IXLAG
+ XC_IYLAG(xc) = DEF_IYLAG
+ XC_XLAG(xc) = DEF_IXLAG
+ XC_YLAG(xc) = DEF_IYLAG
+ XC_DXLAG(xc) = DEF_DXLAG
+ XC_DYLAG(xc) = DEF_DYLAG
+
+ # Define the background fitting parameters.
+ XC_BACKGRD(xc) = XC_BNONE
+ call strcpy ("none", XC_BSTRING(xc), SZ_FNAME)
+ XC_BVALUER(xc) = 0.0
+ XC_BVALUE(xc) = 0.0
+ XC_BORDER(xc) = DEF_BORDER
+ XC_LOREJECT(xc) = DEF_LOREJECT
+ XC_HIREJECT(xc) = DEF_HIREJECT
+ XC_APODIZE(xc) = 0.0
+ XC_FILTER(xc) = XC_FNONE
+ call strcpy ("none", XC_FSTRING(xc), SZ_FNAME)
+
+ # Get the correlation parameters.
+ XC_CFUNC(xc) = cfunc
+ switch (cfunc) {
+ case XC_DISCRETE:
+ call strcpy ("discrete", XC_CSTRING(xc), SZ_FNAME)
+ case XC_FOURIER:
+ call strcpy ("fourier", XC_CSTRING(xc), SZ_FNAME)
+ case XC_FILE:
+ call strcpy ("file", XC_CSTRING(xc), SZ_FNAME)
+ case XC_DIFFERENCE:
+ call strcpy ("difference", XC_CSTRING(xc), SZ_FNAME)
+ default:
+ call strcpy ("unknown", XC_CSTRING(xc), SZ_FNAME)
+ }
+ XC_XWINDOW(xc) = DEF_XWINDOW
+ XC_YWINDOW(xc) = DEF_YWINDOW
+ XC_XCOR(xc) = NULL
+
+ # Define the peak fitting function.
+ XC_PFUNC(xc) = DEF_PFUNC
+ call sprintf (XC_PSTRING(xc), SZ_FNAME, "%s")
+ call pargstr ("centroid")
+ XC_XCBOX(xc) = DEF_XCBOX
+ XC_YCBOX(xc) = DEF_YCBOX
+
+ # Initialize the strings.
+ XC_IMAGE(xc) = EOS
+ XC_REFIMAGE(xc) = EOS
+ XC_REGIONS(xc) = EOS
+ XC_DATABASE(xc) = EOS
+ XC_OUTIMAGE(xc) = EOS
+ XC_REFFILE(xc) = EOS
+ XC_RECORD(xc) = EOS
+
+ # Initialize the buffers.
+ call rg_xrinit (xc)
+
+end
+
+
+# RG_XRINIT -- Initialize the regions definition portion of the
+# cross correlation code fitting structure.
+
+procedure rg_xrinit (xc)
+
+pointer xc #I pointer to crosscor structure
+
+begin
+ call rg_xrfree (xc)
+
+ XC_NREGIONS(xc) = 0
+ XC_CREGION(xc) = 1
+
+ call malloc (XC_RC1(xc), MAX_NREGIONS, TY_INT)
+ call malloc (XC_RC2(xc), MAX_NREGIONS, TY_INT)
+ call malloc (XC_RL1(xc), MAX_NREGIONS, TY_INT)
+ call malloc (XC_RL2(xc), MAX_NREGIONS, TY_INT)
+ call malloc (XC_RZERO(xc), MAX_NREGIONS, TY_REAL)
+ call malloc (XC_RXSLOPE(xc), MAX_NREGIONS, TY_REAL)
+ call malloc (XC_RYSLOPE(xc), MAX_NREGIONS, TY_REAL)
+ call malloc (XC_XSHIFTS(xc), MAX_NREGIONS, TY_REAL)
+ call malloc (XC_YSHIFTS(xc), MAX_NREGIONS, TY_REAL)
+
+ call amovki (INDEFI, Memi[XC_RC1(xc)], MAX_NREGIONS)
+ call amovki (INDEFI, Memi[XC_RC2(xc)], MAX_NREGIONS)
+ call amovki (INDEFI, Memi[XC_RL1(xc)], MAX_NREGIONS)
+ call amovki (INDEFI, Memi[XC_RL2(xc)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[XC_RZERO(xc)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[XC_RXSLOPE(xc)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[XC_RYSLOPE(xc)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[XC_XSHIFTS(xc)], MAX_NREGIONS)
+ call amovkr (INDEFR, Memr[XC_YSHIFTS(xc)], MAX_NREGIONS)
+
+ XC_TXSHIFT(xc) = 0.0
+ XC_TYSHIFT(xc) = 0.0
+end
+
+
+# RG_XCINDEFR -- Re-initialize the background and answers regions portion of
+# the cross-correlation fitting structure
+
+procedure rg_xcindefr (xc, creg)
+
+pointer xc #I pointer to the cross-correlation structure
+int creg #I the current region
+
+int nregions
+int rg_xstati()
+
+begin
+ nregions = rg_xstati (xc, NREGIONS)
+ if (creg < 1 || creg > nregions)
+ return
+
+ if (nregions > 0) {
+ Memr[XC_RZERO(xc)+creg-1] = INDEFR
+ Memr[XC_RXSLOPE(xc)+creg-1] = INDEFR
+ Memr[XC_RYSLOPE(xc)+creg-1] = INDEFR
+ Memr[XC_XSHIFTS(xc)+creg-1] = INDEFR
+ Memr[XC_YSHIFTS(xc)+creg-1] = INDEFR
+ }
+
+ XC_TXSHIFT(xc) = 0.0
+ XC_TYSHIFT(xc) = 0.0
+end
+
+
+# RG_XINDEFR -- Re-initialize the background and answers regions portion of
+# the cross-correlation fitting structure for all regions and reset the
+# current region to 1.
+
+procedure rg_xindefr (xc)
+
+pointer xc #I pointer to the cross-correlation structure
+
+int nregions
+int rg_xstati()
+
+begin
+ nregions = rg_xstati (xc, NREGIONS)
+
+ if (nregions > 0) {
+ call amovkr (INDEFR, Memr[XC_RZERO(xc)], nregions)
+ call amovkr (INDEFR, Memr[XC_RXSLOPE(xc)], nregions)
+ call amovkr (INDEFR, Memr[XC_RYSLOPE(xc)], nregions)
+ call amovkr (INDEFR, Memr[XC_XSHIFTS(xc)], nregions)
+ call amovkr (INDEFR, Memr[XC_YSHIFTS(xc)], nregions)
+ }
+
+ XC_CREGION(xc) = 1
+ XC_TXSHIFT(xc) = 0.0
+ XC_TYSHIFT(xc) = 0.0
+end
+
+
+# RG_XREALLOC -- Reallocate the regions bufffers and initialize if necessary.
+
+procedure rg_xrealloc (xc, nregions)
+
+pointer xc #I pointer to crosscor structure
+int nregions #I number of regions
+
+int nr
+int rg_xstati()
+
+begin
+ nr = rg_xstati (xc, NREGIONS)
+
+ call realloc (XC_RC1(xc), nregions, TY_INT)
+ call realloc (XC_RC2(xc), nregions, TY_INT)
+ call realloc (XC_RL1(xc), nregions, TY_INT)
+ call realloc (XC_RL2(xc), nregions, TY_INT)
+ call realloc (XC_RZERO(xc), nregions, TY_REAL)
+ call realloc (XC_RXSLOPE(xc), nregions, TY_REAL)
+ call realloc (XC_RYSLOPE(xc), nregions, TY_REAL)
+ call realloc (XC_XSHIFTS(xc), nregions, TY_REAL)
+ call realloc (XC_YSHIFTS(xc), nregions, TY_REAL)
+
+ call amovki (INDEFI, Memi[XC_RC1(xc)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[XC_RC2(xc)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[XC_RL1(xc)+nr], nregions - nr)
+ call amovki (INDEFI, Memi[XC_RL2(xc)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[XC_RZERO(xc)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[XC_RXSLOPE(xc)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[XC_RYSLOPE(xc)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[XC_XSHIFTS(xc)+nr], nregions - nr)
+ call amovkr (INDEFR, Memr[XC_YSHIFTS(xc)+nr], nregions - nr)
+end
+
+
+# RG_XFREE -- Free the cross-correlation fitting structure.
+
+procedure rg_xfree (xc)
+
+pointer xc #I pointer to the cross-correlation structure
+
+begin
+ # Free the region descriptors.
+ call rg_xrfree (xc)
+
+ # Free the transformation descriptors.
+ if (XC_XREF(xc) != NULL)
+ call mfree (XC_XREF(xc), TY_REAL)
+ if (XC_YREF(xc) != NULL)
+ call mfree (XC_YREF(xc), TY_REAL)
+ if (XC_TRANSFORM(xc) != NULL)
+ call mfree (XC_TRANSFORM(xc), TY_REAL)
+
+ # Free the correlation function.
+ if (XC_XCOR(xc) != NULL)
+ call mfree (XC_XCOR(xc), TY_REAL)
+
+ call mfree (xc, TY_STRUCT)
+end
+
+
+# RG_XRFREE -- Free the regions portion of the cross-correlation structure.
+
+procedure rg_xrfree (xc)
+
+pointer xc #I pointer to the cross-correlation structure
+
+begin
+ call rg_xseti (xc, NREGIONS, 0)
+ if (XC_RC1(xc) != NULL)
+ call mfree (XC_RC1(xc), TY_INT)
+ XC_RC1(xc) = NULL
+ if (XC_RC2(xc) != NULL)
+ call mfree (XC_RC2(xc), TY_INT)
+ XC_RC2(xc) = NULL
+ if (XC_RL1(xc) != NULL)
+ call mfree (XC_RL1(xc), TY_INT)
+ XC_RL1(xc) = NULL
+ if (XC_RL2(xc) != NULL)
+ call mfree (XC_RL2(xc), TY_INT)
+ XC_RL2(xc) = NULL
+ if (XC_RZERO(xc) != NULL)
+ call mfree (XC_RZERO(xc), TY_REAL)
+ XC_RZERO(xc) = NULL
+ if (XC_RXSLOPE(xc) != NULL)
+ call mfree (XC_RXSLOPE(xc), TY_REAL)
+ XC_RXSLOPE(xc) = NULL
+ if (XC_RYSLOPE(xc) != NULL)
+ call mfree (XC_RYSLOPE(xc), TY_REAL)
+ XC_RYSLOPE(xc) = NULL
+ if (XC_XSHIFTS(xc) != NULL)
+ call mfree (XC_XSHIFTS(xc), TY_REAL)
+ XC_XSHIFTS(xc) = NULL
+ if (XC_YSHIFTS(xc) != NULL)
+ call mfree (XC_YSHIFTS(xc), TY_REAL)
+ XC_YSHIFTS(xc) = NULL
+end
+
+
+# RG_XSTATI -- Fetch the value of a cross-correlation fitting structure
+# integer parameter.
+
+int procedure rg_xstati (xc, param)
+
+pointer xc #I pointer to the cross-correlation fitting structure
+int param #I parameter to be fetched
+
+begin
+ switch (param) {
+ case CFUNC:
+ return (XC_CFUNC(xc))
+ case IXLAG:
+ return (XC_IXLAG(xc))
+ case IYLAG:
+ return (XC_IYLAG(xc))
+ case XLAG:
+ return (XC_XLAG(xc))
+ case YLAG:
+ return (XC_YLAG(xc))
+ case DXLAG:
+ return (XC_DXLAG(xc))
+ case DYLAG:
+ return (XC_DYLAG(xc))
+ case XWINDOW:
+ return (XC_XWINDOW(xc))
+ case YWINDOW:
+ return (XC_YWINDOW(xc))
+ case CREGION:
+ return (XC_CREGION(xc))
+ case NREGIONS:
+ return (XC_NREGIONS(xc))
+ case BACKGRD:
+ return (XC_BACKGRD(xc))
+ case BORDER:
+ return (XC_BORDER(xc))
+ case FILTER:
+ return (XC_FILTER(xc))
+ case XCBOX:
+ return (XC_XCBOX(xc))
+ case YCBOX:
+ return (XC_YCBOX(xc))
+ case PFUNC:
+ return (XC_PFUNC(xc))
+ case NREFPTS:
+ return (XC_NREFPTS(xc))
+ default:
+ call error (0, "RG_XSTATI: Undefined integer parameter.")
+ }
+end
+
+
+# RG_XSTATP -- Fetch the value of a pointer parameter.
+
+pointer procedure rg_xstatp (xc, param)
+
+pointer xc #I pointer to the cross-correlation structure
+int param #I parameter to be fetched
+
+begin
+ switch (param) {
+ case RC1:
+ return (XC_RC1(xc))
+ case RC2:
+ return (XC_RC2(xc))
+ case RL1:
+ return (XC_RL1(xc))
+ case RL2:
+ return (XC_RL2(xc))
+ case RZERO:
+ return (XC_RZERO(xc))
+ case RXSLOPE:
+ return (XC_RXSLOPE(xc))
+ case RYSLOPE:
+ return (XC_RYSLOPE(xc))
+ case XSHIFTS:
+ return (XC_XSHIFTS(xc))
+ case YSHIFTS:
+ return (XC_YSHIFTS(xc))
+ case XCOR:
+ return (XC_XCOR(xc))
+ case XREF:
+ return (XC_XREF(xc))
+ case YREF:
+ return (XC_YREF(xc))
+# case CORAPODIZE:
+# return (XC_CORAPODIZE(xc))
+ case TRANSFORM:
+ return (XC_TRANSFORM(xc))
+ default:
+ call error (0, "RG_XSTATP: Undefined pointer parameter.")
+ }
+end
+
+
+# RG_XSTATR -- Fetch the value of a real parameter.
+
+real procedure rg_xstatr (xc, param)
+
+pointer xc #I pointer to the cross-correlation structure
+int param #I parameter to be fetched
+
+begin
+ switch (param) {
+ case BVALUER:
+ return (XC_BVALUER(xc))
+ case BVALUE:
+ return (XC_BVALUE(xc))
+ case LOREJECT:
+ return (XC_LOREJECT(xc))
+ case HIREJECT:
+ return (XC_HIREJECT(xc))
+ case APODIZE:
+ return (XC_APODIZE(xc))
+ case TXSHIFT:
+ return (XC_TXSHIFT(xc))
+ case TYSHIFT:
+ return (XC_TYSHIFT(xc))
+ default:
+ call error (0, "RG_XSTATR: Undefined real parameter.")
+ }
+end
+
+
+# RG_XSTATS -- Fetch the value of a string parameter.
+
+procedure rg_xstats (xc, param, str, maxch)
+
+pointer xc #I pointer to the cross-correlation structure
+int param #I parameter to be fetched
+char str[ARB] #O output value of string parameter
+int maxch #I maximum number of characters in output string
+
+begin
+ switch (param) {
+ case BSTRING:
+ call strcpy (XC_BSTRING(xc), str, maxch)
+ case FSTRING:
+ call strcpy (XC_FSTRING(xc), str, maxch)
+ case CSTRING:
+ call strcpy (XC_CSTRING(xc), str, maxch)
+ case PSTRING:
+ call strcpy (XC_PSTRING(xc), str, maxch)
+ case REFIMAGE:
+ call strcpy (XC_REFIMAGE(xc), str, maxch)
+ case IMAGE:
+ call strcpy (XC_IMAGE(xc), str, maxch)
+ case OUTIMAGE:
+ call strcpy (XC_OUTIMAGE(xc), str, maxch)
+ case REGIONS:
+ call strcpy (XC_REGIONS(xc), str, maxch)
+ case DATABASE:
+ call strcpy (XC_DATABASE(xc), str, maxch)
+ case RECORD:
+ call strcpy (XC_RECORD(xc), str, maxch)
+ case REFFILE:
+ call strcpy (XC_REFFILE(xc), str, maxch)
+ default:
+ call error (0, "RG_XSTATS: Undefined string parameter.")
+ }
+end
+
+
+# RG_XSETI -- Set the value of an integer parameter.
+
+procedure rg_xseti (xc, param, value)
+
+pointer xc #I pointer to the cross-correlation structure
+int param #I parameter to be set
+int value #O value of the integer parameter
+
+begin
+ switch (param) {
+ case CFUNC:
+ XC_CFUNC(xc) = value
+ switch (value) {
+ case XC_DISCRETE:
+ call strcpy ("discrete", XC_CSTRING(xc), SZ_FNAME)
+ case XC_FOURIER:
+ call strcpy ("fourier", XC_CSTRING(xc), SZ_FNAME)
+ case XC_FILE:
+ call strcpy ("file", XC_CSTRING(xc), SZ_FNAME)
+ case XC_DIFFERENCE:
+ call strcpy ("difference", XC_CSTRING(xc), SZ_FNAME)
+ default:
+ call strcpy ("unknown", XC_CSTRING(xc), SZ_FNAME)
+ }
+ case IXLAG:
+ XC_IXLAG(xc) = value
+ case IYLAG:
+ XC_IYLAG(xc) = value
+ case XLAG:
+ XC_XLAG(xc) = value
+ case YLAG:
+ XC_YLAG(xc) = value
+ case DXLAG:
+ XC_DXLAG(xc) = value
+ case DYLAG:
+ XC_DYLAG(xc) = value
+ case XWINDOW:
+ XC_XWINDOW(xc) = value
+ case YWINDOW:
+ XC_YWINDOW(xc) = value
+ case BACKGRD:
+ XC_BACKGRD(xc) = value
+ switch (value) {
+ case XC_BNONE:
+ call strcpy ("none", XC_BSTRING(xc), SZ_FNAME)
+ case XC_MEAN:
+ call strcpy ("mean", XC_BSTRING(xc), SZ_FNAME)
+ case XC_MEDIAN:
+ call strcpy ("median", XC_BSTRING(xc), SZ_FNAME)
+ case XC_SLOPE:
+ call strcpy ("plane", XC_BSTRING(xc), SZ_FNAME)
+ default:
+ call strcpy ("none", XC_BSTRING(xc), SZ_FNAME)
+ }
+ case BORDER:
+ XC_BORDER(xc) = value
+ case FILTER:
+ XC_FILTER(xc) = value
+ switch (value) {
+ case XC_FNONE:
+ call strcpy ("none", XC_FSTRING(xc), SZ_FNAME)
+ case XC_LAPLACE:
+ call strcpy ("laplace", XC_FSTRING(xc), SZ_FNAME)
+ default:
+ call strcpy ("none", XC_FSTRING(xc), SZ_FNAME)
+ }
+ case XCBOX:
+ XC_XCBOX(xc) = value
+ case YCBOX:
+ XC_YCBOX(xc) = value
+ case PFUNC:
+ XC_PFUNC(xc) = value
+ switch (value) {
+ case XC_PNONE:
+ call strcpy ("none", XC_PSTRING(xc), SZ_FNAME)
+ case XC_CENTROID:
+ call strcpy ("centroid", XC_PSTRING(xc), SZ_FNAME)
+ case XC_PARABOLA:
+ call strcpy ("parabolic", XC_PSTRING(xc), SZ_FNAME)
+ case XC_SAWTOOTH:
+ call strcpy ("sawtooth", XC_PSTRING(xc), SZ_FNAME)
+# case XC_MARK:
+# call strcpy ("mark", XC_PSTRING(xc), SZ_FNAME)
+ default:
+ ;
+ }
+ case NREFPTS:
+ XC_NREFPTS(xc) = value
+ case CREGION:
+ XC_CREGION(xc) = value
+ case NREGIONS:
+ XC_NREGIONS(xc) = value
+ default:
+ call error (0, "RG_XSETI: Undefined integer parameter.")
+ }
+end
+
+
+# RG_XSETP -- Set the value of a pointer parameter.
+
+procedure rg_xsetp (xc, param, value)
+
+pointer xc #I pointer to the cross-correlation structure
+int param #I parameter to be set
+pointer value #O value of the pointer parameter
+
+begin
+ switch (param) {
+ case RC1:
+ XC_RC1(xc) = value
+ case RC2:
+ XC_RC2(xc) = value
+ case RL1:
+ XC_RL1(xc) = value
+ case RL2:
+ XC_RL2(xc) = value
+ case RZERO:
+ XC_RZERO(xc) = value
+ case RXSLOPE:
+ XC_RXSLOPE(xc) = value
+ case RYSLOPE:
+ XC_RYSLOPE(xc) = value
+ case XSHIFTS:
+ XC_XSHIFTS(xc) = value
+ case YSHIFTS:
+ XC_YSHIFTS(xc) = value
+ case XCOR:
+ XC_XCOR(xc) = value
+ case XREF:
+ XC_XREF(xc) = value
+ case YREF:
+ XC_YREF(xc) = value
+ case TRANSFORM:
+ XC_TRANSFORM(xc) = value
+# case CORAPODIZE:
+# XC_CORAPODIZE(xc) = value
+ default:
+ call error (0, "RG_XSETP: Undefined pointer parameter.")
+ }
+end
+
+
+# RG_XSETR -- Set the value of a real parameter.
+
+procedure rg_xsetr (xc, param, value)
+
+pointer xc #I pointer to the cross-correlation structure
+int param #I parameter to be set
+real value #O value of real parameter
+
+begin
+ switch (param) {
+ case BVALUER:
+ XC_BVALUER(xc) = value
+ case BVALUE:
+ XC_BVALUE(xc) = value
+ case LOREJECT:
+ XC_LOREJECT(xc) = value
+ case HIREJECT:
+ XC_HIREJECT(xc) = value
+ case APODIZE:
+ XC_APODIZE(xc) = value
+ case TXSHIFT:
+ XC_TXSHIFT(xc) = value
+ case TYSHIFT:
+ XC_TYSHIFT(xc) = value
+ default:
+ call error (0, "RG_XSETR: Undefined real parameter.")
+ }
+end
+
+
+# RG_XSETS -- Set the value of a string parameter.
+
+procedure rg_xsets (xc, param, str)
+
+pointer xc #I pointer to the cross-correlation structure
+int param #I parameter to be set
+char str[ARB] #O value of string parameter
+
+int index
+pointer sp, temp
+int strdic(), fnldir()
+
+begin
+ call smark (sp)
+ call salloc (temp, SZ_FNAME, TY_CHAR)
+
+ switch (param) {
+ case BSTRING:
+ index = strdic (str, str, SZ_LINE, XC_BTYPES)
+ if (index > 0) {
+ call strcpy (str, XC_BSTRING(xc), SZ_FNAME)
+ call rg_xseti (xc, BACKGRD, index)
+ }
+ case FSTRING:
+ index = strdic (str, str, SZ_LINE, XC_FTYPES)
+ if (index > 0) {
+ call strcpy (str, XC_FSTRING(xc), SZ_FNAME)
+ call rg_xseti (xc, FILTER, index)
+ }
+ case CSTRING:
+ index = strdic (str, str, SZ_LINE, XC_CTYPES)
+ if (index > 0) {
+ call strcpy (str, XC_CSTRING(xc), SZ_FNAME)
+ call rg_xseti (xc, CFUNC, index)
+ }
+ case PSTRING:
+ call strcpy (str, XC_PSTRING(xc), SZ_FNAME)
+ case REFIMAGE:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], XC_REFIMAGE(xc), SZ_FNAME)
+ call strcpy (Memc[temp+index], XC_REFIMAGE(xc), SZ_FNAME)
+ case IMAGE:
+ call imgcluster (str, Memc[temp], SZ_FNAME)
+ index = fnldir (Memc[temp], XC_IMAGE(xc), SZ_FNAME)
+ call strcpy (Memc[temp+index], XC_IMAGE(xc), SZ_FNAME)
+ case OUTIMAGE:
+ call strcpy (str, XC_OUTIMAGE(xc), SZ_FNAME)
+ case REGIONS:
+ call strcpy (str, XC_REGIONS(xc), SZ_FNAME)
+ case DATABASE:
+ index = fnldir (str, XC_DATABASE(xc), SZ_FNAME)
+ call strcpy (str[index+1], XC_DATABASE(xc), SZ_FNAME)
+ case RECORD:
+ call strcpy (str, XC_RECORD(xc), SZ_FNAME)
+ case REFFILE:
+ index = fnldir (str, XC_REFFILE(xc), SZ_FNAME)
+ call strcpy (str[index+1], XC_REFFILE(xc), SZ_FNAME)
+ default:
+ call error (0, "RG_XSETS: Undefined string parameter.")
+ }
+
+ call sfree (sp)
+end
diff --git a/pkg/images/immatch/src/xregister/rgxtransform.x b/pkg/images/immatch/src/xregister/rgxtransform.x
new file mode 100644
index 00000000..63ee5f24
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/rgxtransform.x
@@ -0,0 +1,446 @@
+include <imhdr.h>
+include <math.h>
+include "xregister.h"
+
+# RG_GXTRANSFORM -- Open the reference points file and the read the
+# coordinates of the reference points in the reference image. Return
+# the reference points file name and descriptor.
+
+int procedure rg_gxtransform (list, xc, reffile)
+
+int list #I list of reference points files
+pointer xc #I pointer to the cross-correlation structure
+char reffile[ARB] #O the output reference points file name
+
+int tdf
+pointer sp, line, pxref, pyref
+real x1, y1, x2, y2, x3, y3
+int fntgfnb(), open(), getline(), nscan()
+pointer rg_xstatp()
+
+begin
+ # Get some working memory.
+ call smark (sp)
+ call salloc (line, SZ_LINE, TY_CHAR)
+
+ # Get the points to the reference point lists.
+ pxref = rg_xstatp (xc, XREF)
+ pyref = rg_xstatp (xc, YREF)
+ call aclrr (Memr[rg_xstatp(xc, XREF)], MAX_NREF)
+ call aclrr (Memr[rg_xstatp(xc, YREF)], MAX_NREF)
+
+ # Open the reference points file and read the coordinates.
+ while (fntgfnb (list, reffile, SZ_FNAME) != EOF) {
+
+ iferr {
+
+ # Open the reference file.
+ tdf = open (reffile, READ_ONLY, TEXT_FILE)
+ call aclrr (Memr[pxref], MAX_NREF)
+ call aclrr (Memr[pyref], MAX_NREF)
+
+ # Read up to three valid reference points from the list.
+ while (getline (tdf, Memc[line]) != EOF) {
+ call sscan (Memc[line])
+ call gargr (x1)
+ call gargr (y1)
+ call gargr (x2)
+ call gargr (y2)
+ call gargr (x3)
+ call gargr (y3)
+ if (nscan () >= 2)
+ break
+ }
+
+ # Store the reference points.
+ if (nscan () == 2) {
+ Memr[pxref] = x1
+ Memr[pyref] = y1
+ call rg_xseti (xc, NREFPTS, 1)
+ } else if (nscan () == 4) {
+ Memr[pxref] = x1
+ Memr[pyref] = y1
+ Memr[pxref+1] = x2
+ Memr[pyref+1] = y2
+ call rg_xseti (xc, NREFPTS, 2)
+ } else if (nscan () == 6) {
+ Memr[pxref] = x1
+ Memr[pyref] = y1
+ Memr[pxref+1] = x2
+ Memr[pyref+1] = y2
+ Memr[pxref+2] = x3
+ Memr[pyref+2] = y3
+ call rg_xseti (xc, NREFPTS, 2)
+ } else
+ call rg_xseti (xc, NREFPTS, 0)
+
+ } then {
+ call rg_xseti (xc, NREFPTS, 0)
+ }
+ }
+
+ call sfree (sp)
+
+ return (tdf)
+end
+
+
+# RG_ITRANSFORM -- Compute the transformation from the input image to the
+# reference image interactively.
+
+procedure rg_itransform (xc, imr, im, id)
+
+pointer xc #I pointer to the cross-correlation stucture
+pointer imr #I pointer to the reference image
+pointer im #I pointer to the input image
+pointer id #I pointer to the display device
+
+int nref, nstar, wcs, key
+pointer sp, cmd, x, y, pxref, pyref, ptrans
+real wx, wy
+int clgcur()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (cmd, SZ_LINE, TY_CHAR)
+ call salloc (x, MAX_NREF, TY_REAL)
+ call salloc (y, MAX_NREF, TY_REAL)
+ call aclrr (Memr[x], MAX_NREF)
+ call aclrr (Memr[y], MAX_NREF)
+
+ # Get the pointers.
+ pxref = rg_xstatp (xc, XREF)
+ pyref = rg_xstatp (xc, YREF)
+ ptrans = rg_xstatp (xc, TRANSFORM)
+
+ # Mark up to three reference stars.
+ nref = 0
+ call printf ("Mark reference star %d with the image cursor [q=quit]: ")
+ call pargi (nref + 1)
+ while ((nref < MAX_NREF) && clgcur ("icommands", wx, wy, wcs, key,
+ Memc[cmd], SZ_LINE) != EOF) {
+ if (key == 'q') {
+ call printf ("\n")
+ break
+ }
+ if (wx < 0.5 || wx > IM_LEN(imr,1) + 0.5) {
+ call printf ("\n")
+ next
+ }
+ if (wy < 0.5 || wy > IM_LEN(imr,2) + 0.5) {
+ call printf ("\n")
+ next
+ }
+ call printf ("%g %g\n")
+ call pargr (wx)
+ call pargr (wy)
+ Memr[pxref+nref] = wx
+ Memr[pyref+nref] = wy
+ nref = nref + 1
+ call rg_xseti (xc, NREFPTS, nref)
+ if (nref >= MAX_NREF)
+ break
+ call printf (
+ "Mark reference star %d with the image cursor [q=quit]: ")
+ call pargi (nref + 1)
+ }
+
+ # Mark the corresponding input image stars.
+ if (nref > 0) {
+
+ nstar = 0
+ call printf ("Mark image star %d with the image cursor [q=quit]: ")
+ call pargi (nstar + 1)
+ while ((nstar < nref) && clgcur ("icommands", wx, wy, wcs, key,
+ Memc[cmd], SZ_LINE) != EOF) {
+ if (key == 'q') {
+ call printf ("\n")
+ break
+ }
+ if (wx < 0.5 || wx > IM_LEN(im,1) + 0.5) {
+ call printf ("\n")
+ next
+ }
+ if (wy < 0.5 || wy > IM_LEN(im,2) + 0.5) {
+ call printf ("\n")
+ next
+ }
+ call printf ("%g %g\n")
+ call pargr (wx)
+ call pargr (wy)
+ Memr[x+nstar] = wx
+ Memr[y+nstar] = wy
+ nstar = nstar + 1
+ if (nstar >= MAX_NREF)
+ break
+ call printf (
+ "Mark image star %d with the image cursor [q=quit]: ")
+ call pargi (nstar + 1)
+ }
+
+ # Compute the transformation.
+ if (nstar > 0) {
+ switch (nstar) {
+ case 0:
+ call rg_xshift (Memr[pxref], Memr[pyref], Memr[pxref],
+ Memr[pyref], Memr[ptrans])
+ case 1:
+ call rg_xshift (Memr[x], Memr[y], Memr[pxref], Memr[pyref],
+ Memr[ptrans])
+ #case 2:
+ #call rg_xtwostar (Memr[x], Memr[y], Memr[pxref],
+ #Memr[pyref], Memr[ptrans])
+ #case 3:
+ #call rg_xthreestar (Memr[x], Memr[y], Memr[pxref],
+ #Memr[pyref], Memr[ptrans])
+
+ default:
+ call rg_xshift (Memr[pxref], Memr[pyref], Memr[pxref],
+ Memr[pyref], Memr[ptrans])
+ }
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_XTRANSFORM -- Compute the transformation from the input image to
+# the reference image
+
+procedure rg_xtransform (tfd, xc)
+
+int tfd #I the reference points file descriptor
+pointer xc #I the cross-correlation file descriptor
+
+int nref
+pointer sp, line, x, y, pxref, pyref, ptrans
+int getline(), rg_xstati(), nscan()
+pointer rg_xstatp()
+
+begin
+ # Allocate working space.
+ call smark (sp)
+ call salloc (line, SZ_LINE, TY_CHAR)
+ call salloc (x, MAX_NREF, TY_REAL)
+ call salloc (y, MAX_NREF, TY_REAL)
+ call aclrr (Memr[x], MAX_NREF)
+ call aclrr (Memr[y], MAX_NREF)
+
+ # Get the pointers to the reference image data.
+ nref = rg_xstati (xc, NREFPTS)
+ pxref = rg_xstatp (xc, XREF)
+ pyref = rg_xstatp (xc, YREF)
+ ptrans = rg_xstatp (xc, TRANSFORM)
+
+ # Read the input image reference points.
+ while ((nref > 0) && getline (tfd, Memc[line]) != EOF) {
+ call sscan (Memc[line])
+ call gargr (Memr[x])
+ call gargr (Memr[y])
+ call gargr (Memr[x+1])
+ call gargr (Memr[y+1])
+ call gargr (Memr[x+2])
+ call gargr (Memr[y+2])
+ if (nscan() >= 2 * nref)
+ break
+ }
+
+ # Compute the transform.
+ if (nscan () < 2 * nref) {
+ call rg_xshift (Memr[pxref], Memr[pyref], Memr[pxref], Memr[pyref],
+ Memr[ptrans])
+ } else {
+ switch (nref) {
+ case 0:
+ call rg_xshift (Memr[pxref], Memr[pyref], Memr[pxref],
+ Memr[pyref], Memr[ptrans])
+ case 1:
+ call rg_xshift (Memr[x], Memr[y], Memr[pxref], Memr[pyref],
+ Memr[ptrans])
+ case 2:
+ call rg_xtwostar (Memr[x], Memr[y], Memr[pxref], Memr[pyref],
+ Memr[ptrans])
+ case 3:
+ call rg_xthreestar (Memr[x], Memr[y], Memr[pxref], Memr[pyref],
+ Memr[ptrans])
+ }
+ }
+
+ call sfree (sp)
+end
+
+
+# RG_ETRANSFORM -- Evaulate the current transform at a single point.
+
+procedure rg_etransform (xc, xin, yin, xout, yout)
+
+pointer xc #I pointer to the cross-correlation structure
+real xin, yin #I the input x and y values
+real xout, yout #O the output x and y values
+
+pointer ptrans
+pointer rg_xstatp
+
+begin
+ ptrans = rg_xstatp (xc, TRANSFORM)
+ xout = Memr[ptrans] * xin + Memr[ptrans+1] * yin + Memr[ptrans+2]
+ yout = Memr[ptrans+3] * xin + Memr[ptrans+4] * yin + Memr[ptrans+5]
+end
+
+
+# RG_XSHIFT -- Compute the transformation coefficients required to define a
+# simple shift using a single data point.
+
+procedure rg_xshift (xref, yref, xlist, ylist, coeff)
+
+real xref[ARB] #I x reference coordinates
+real yref[ARB] #I y reference coordinates
+real xlist[ARB] #I x input coordinates
+real ylist[ARB] #I y input coordinates
+real coeff[ARB] #O output coefficient array
+
+begin
+ # Compute the x transformation.
+ coeff[1] = 1.0
+ coeff[2] = 0.0
+ coeff[3] = xref[1] - xlist[1]
+
+ # Compute the y transformation.
+ coeff[4] = 0.0
+ coeff[5] = 1.0
+ coeff[6] = yref[1] - ylist[1]
+end
+
+
+# RG_XTWOSTAR -- Compute the transformation coefficients required to
+# define a simple shift, magnification which is the same in x and y,
+# and rotation using two data points.
+
+procedure rg_xtwostar (xref, yref, xlist, ylist, coeff)
+
+real xref[ARB] #I x reference coordinates
+real yref[ARB] #I y reference coordinates
+real xlist[ARB] #I x input coordinates
+real ylist[ARB] #I y input coordinates
+real coeff[ARB] #O coefficient array
+
+real rot, mag, dxlis, dylis, dxref, dyref, cosrot, sinrot
+real rg_xposangle()
+
+begin
+ # Compute the deltas.
+ dxlis = xlist[2] - xlist[1]
+ dylis = ylist[2] - ylist[1]
+ dxref = xref[2] - xref[1]
+ dyref = yref[2] - yref[1]
+
+ # Compute the required rotation angle.
+ rot = rg_xposangle (dxref, dyref) - rg_xposangle (dxlis, dylis)
+ cosrot = cos (rot)
+ sinrot = sin (rot)
+
+ # Compute the required magnification factor.
+ mag = dxlis ** 2 + dylis ** 2
+ if (mag <= 0.0)
+ mag = 0.0
+ else
+ mag = sqrt ((dxref ** 2 + dyref ** 2) / mag)
+
+ # Compute the transformation coefficicents.
+ coeff[1] = mag * cosrot
+ coeff[2] = - mag * sinrot
+ coeff[3] = xref[1] - mag * cosrot * xlist[1] + mag * sinrot * ylist[1]
+ coeff[4] = mag * sinrot
+ coeff[5] = mag * cosrot
+ coeff[6] = yref[1] - mag * sinrot * xlist[1] - mag * cosrot * ylist[1]
+end
+
+
+# RG_THREESTAR -- Compute the transformation coefficients required to define
+# x and y shifts, x and ymagnifications, a rotation and skew, and a possible
+# axis flip using three tie points.
+
+procedure rg_xthreestar (xref, yref, xlist, ylist, coeff)
+
+real xref[ARB] #I x reference coordinates
+real yref[ARB] #I y reference coordinates
+real xlist[ARB] #I x input coordinates
+real ylist[ARB] #I y input coordinates
+real coeff[ARB] #O coefficient array
+
+real dx23, dx13, dx12, dy23, dy13, dy12, det
+bool fp_equalr()
+
+begin
+ # Compute the deltas.
+ dx23 = xlist[2] - xlist[3]
+ dx13 = xlist[1] - xlist[3]
+ dx12 = xlist[1] - xlist[2]
+ dy23 = ylist[2] - ylist[3]
+ dy13 = ylist[1] - ylist[3]
+ dy12 = ylist[1] - ylist[2]
+
+ # Compute the determinant.
+ det = xlist[1] * dy23 - xlist[2] * dy13 + xlist[3] * dy12
+ if (fp_equalr (det, 0.0)) {
+ call rg_xtwostar (xref, yref, xlist, ylist, coeff)
+ return
+ }
+
+ # Compute the x transformation.
+ coeff[1] = (xref[1] * dy23 - xref[2] * dy13 + xref[3] * dy12) / det
+ coeff[2] = (-xref[1] * dx23 + xref[2] * dx13 - xref[3] * dx12) / det
+ coeff[3] = (xref[1] * (xlist[2] * ylist[3] - xlist[3] * ylist[2]) +
+ xref[2] * (ylist[1] * xlist[3] - xlist[1] * ylist[3]) +
+ xref[3] * (xlist[1] * ylist[2] - ylist[1] * xlist[2])) / det
+
+ # Compute the y transformation.
+ coeff[4] = (yref[1] * dy23 - yref[2] * dy13 + yref[3] * dy12) / det
+ coeff[5] = (-yref[1] * dx23 + yref[2] * dx13 - yref[3] * dx12) / det
+ coeff[6] = (yref[1] * (xlist[2] * ylist[3] - xlist[3] * ylist[2]) +
+ yref[2] * (ylist[1] * xlist[3] - xlist[1] * ylist[3]) +
+ yref[3] * (xlist[1] * ylist[2] - ylist[1] * xlist[2])) / det
+end
+
+
+# RG_XPOSANGLE -- Compute the position angle of a 2D vector. The angle is
+# measured counter-clockwise from the positive x axis.
+
+real procedure rg_xposangle (x, y)
+
+real x #I x vector component
+real y #I y vector component
+
+real theta
+bool fp_equalr()
+
+begin
+ if (fp_equalr (y, 0.0)) {
+ if (x > 0.0)
+ theta = 0.0
+ else if (x < 0.0)
+ theta = PI
+ else
+ theta = 0.0
+ } else if (fp_equalr (x, 0.0)) {
+ if (y > 0.0)
+ theta = PI / 2.0
+ else if (y < 0.0)
+ theta = 3.0 * PI / 2.0
+ else
+ theta = 0.0
+ } else if (x > 0.0 && y > 0.0) { # 1st quadrant
+ theta = atan (y / x)
+ } else if (x > 0.0 && y < 0.0) { # 4th quadrant
+ theta = 2.0 * PI + atan (y / x)
+ } else if (x < 0.0 && y > 0.0) { # 2nd quadrant
+ theta = PI + atan (y / x)
+ } else if (x < 0.0 && y < 0.0) { # 3rd quadrant
+ theta = PI + atan (y / x)
+ }
+
+ return (theta)
+end
diff --git a/pkg/images/immatch/src/xregister/t_xregister.x b/pkg/images/immatch/src/xregister/t_xregister.x
new file mode 100644
index 00000000..f9fc9b22
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/t_xregister.x
@@ -0,0 +1,440 @@
+include <imhdr.h>
+include <fset.h>
+include <gset.h>
+include <imset.h>
+include "xregister.h"
+
+# T_XREGISTER -- Register a list of images using cross-correlation techniques.
+
+procedure t_xregister()
+
+pointer freglist # reference regions list
+pointer database # the shifts database
+int dformat # use the database format for the shifts file ?
+int interactive # interactive mode ?
+int verbose # verbose mode
+pointer interpstr # interpolant type
+int boundary # boundary extension type
+real constant # constant for boundary extension
+
+int list1, listr, list2, reglist, reflist, reclist, tfd, stat, nregions
+int c1, c2, l1, l2, ncols, nlines
+pointer sp, image1, image2, imtemp, str, coords
+pointer gd, id, imr, im1, im2, sdb, xc, mw
+real shifts[2]
+bool clgetb()
+int imtopen(), imtlen(), imtgetim(), fntopnb(), clgwrd(), btoi()
+int rg_xregions(), fntlenb(), rg_gxtransform(), rg_xstati()
+int rg_xcorr(), rg_xicorr(), fntgfnb(), access(), open()
+pointer gopen(), immap(), dtmap(), mw_openim()
+real clgetr(), rg_xstatr()
+errchk fntopnb(), gopen()
+
+begin
+ # Set STDOUT to flush on a newline character
+ call fseti (STDOUT, F_FLUSHNL, YES)
+
+ # Allocate temporary working space.
+ call smark (sp)
+
+ call salloc (freglist, SZ_LINE, TY_CHAR)
+ call salloc (image1, SZ_FNAME, TY_CHAR)
+ call salloc (image2, SZ_FNAME, TY_CHAR)
+ call salloc (imtemp, SZ_FNAME, TY_CHAR)
+ call salloc (database, SZ_FNAME, TY_CHAR)
+ call salloc (coords, SZ_FNAME, TY_CHAR)
+ call salloc (interpstr, SZ_FNAME, TY_CHAR)
+ call salloc (str, SZ_LINE, TY_CHAR)
+
+ # Get task parameters and open lists.
+ call clgstr ("input", Memc[str], SZ_LINE)
+ list1 = imtopen (Memc[str])
+ call clgstr ("reference", Memc[str], SZ_LINE)
+ listr = imtopen (Memc[str])
+ call clgstr ("regions", Memc[freglist], SZ_LINE)
+ call clgstr ("shifts", Memc[database], SZ_FNAME)
+ call clgstr ("output", Memc[str], SZ_LINE)
+ list2 = imtopen (Memc[str])
+ call clgstr ("records", Memc[str], SZ_LINE)
+ if (Memc[str] == EOS)
+ reclist = NULL
+ else
+ reclist = fntopnb (Memc[str], NO)
+ call clgstr ("coords", Memc[coords], SZ_LINE)
+
+ # Open the cross correlation fitting structure.
+ call rg_xgpars (xc)
+
+ # Test the reference image list length.
+ if (rg_xstati (xc, CFUNC) != XC_FILE) {
+ if (imtlen (listr) <= 0)
+ call error (0, "The reference image list is empty.")
+ if (imtlen (listr) > 1 && imtlen (listr) != imtlen (list1))
+ call error (0,
+ "The number of reference and input images is not the same.")
+ if (Memc[coords] == EOS)
+ reflist = NULL
+ else {
+ reflist = fntopnb (Memc[coords], NO)
+ if (imtlen (listr) != fntlenb (reflist))
+ call error (0,
+ "The number of reference point files and images is not the same.")
+ }
+ iferr {
+ reglist = fntopnb (Memc[freglist], NO)
+ } then {
+ reglist = NULL
+ }
+ call rg_xsets (xc, REGIONS, Memc[freglist])
+
+ } else {
+ call imtclose (listr)
+ listr = NULL
+ reflist = NULL
+ reglist = NULL
+ call rg_xsets (xc, REGIONS, "")
+ }
+
+ # Close the output image list if it is empty.
+ if (imtlen (list2) == 0) {
+ call imtclose (list2)
+ list2 = NULL
+ }
+
+ # Check that the output image list is the same size as the input
+ # image list.
+ if (list2 != NULL) {
+ if (imtlen (list1) != imtlen (list2)) {
+ call imtclose (list1)
+ if (list2 != NULL)
+ call imtclose (list2)
+ call error (0,
+ "The number of input and output images is not the same.")
+ }
+ }
+
+ # Check that the record list is the same length as the input
+ # image list length.
+ if (reclist != NULL) {
+ if (fntlenb (reclist) != imtlen (list1))
+ call error (0,
+ "Input image and record lists are not the same length.")
+ }
+
+
+ # Open the database file.
+ dformat = btoi (clgetb ("databasefmt"))
+ if (rg_xstati (xc, CFUNC) == XC_FILE) {
+ if (dformat == YES)
+ sdb = dtmap (Memc[database], READ_ONLY)
+ else
+ sdb = open (Memc[database], READ_ONLY, TEXT_FILE)
+ } else if (clgetb ("append")) {
+ if (dformat == YES)
+ sdb = dtmap (Memc[database], APPEND)
+ else
+ sdb = open (Memc[database], NEW_FILE, TEXT_FILE)
+ } else if (access (Memc[database], 0, 0) == YES) {
+ call error (0, "The shifts database file already exists")
+ } else {
+ if (dformat == YES)
+ sdb = dtmap (Memc[database], NEW_FILE)
+ else
+ sdb = open (Memc[database], NEW_FILE, TEXT_FILE)
+ }
+ call rg_xsets (xc, DATABASE, Memc[database])
+
+ # Get the boundary extension parameters for the image shift.
+ call clgstr ("interp_type", Memc[interpstr], SZ_FNAME)
+ boundary = clgwrd ("boundary_type", Memc[str], SZ_LINE,
+ "|constant|nearest|reflect|wrap|")
+ constant = clgetr ("constant")
+
+ if (rg_xstati (xc, CFUNC) == XC_FILE)
+ interactive = NO
+ else
+ interactive = btoi (clgetb ("interactive"))
+ if (interactive == YES) {
+ call clgstr ("graphics", Memc[str], SZ_FNAME)
+ iferr (gd = gopen (Memc[str], NEW_FILE, STDGRAPH))
+ gd = NULL
+ call clgstr ("display", Memc[str], SZ_FNAME)
+ iferr (id = gopen (Memc[str], APPEND, STDIMAGE))
+ id = NULL
+ verbose = YES
+ } else {
+ if (rg_xstati (xc, PFUNC) == XC_MARK)
+ call rg_xseti (xc, PFUNC, XC_CENTROID)
+ gd = NULL
+ id = NULL
+ verbose = btoi (clgetb ("verbose"))
+ }
+
+ # Initialize the reference image filter descriptors
+ imr = NULL
+ tfd = NULL
+
+ # Initialize the overlap section.
+ c1 = INDEFI
+ c2 = INDEFI
+ l1 = INDEFI
+ l2 = INDEFI
+ ncols = INDEFI
+ nlines = INDEFI
+
+ # Do each set of input, reference, and output images.
+ while ((imtgetim (list1, Memc[image1], SZ_FNAME) != EOF)) {
+
+ # Open the reference image, and associated regions and coordinates
+ # files if the correlation function is not file.
+
+ if (rg_xstati (xc, CFUNC) != XC_FILE) {
+ if (imtgetim (listr, Memc[str], SZ_FNAME) != EOF) {
+ if (imr != NULL)
+ call imunmap (imr)
+ imr = immap (Memc[str], READ_ONLY, 0)
+ if (IM_NDIM(imr) > 2)
+ call error (0, "Reference images must be 1D or 2D")
+ call rg_xsets (xc, REFIMAGE, Memc[str])
+ nregions = rg_xregions (reglist, imr, xc, 1)
+ if (nregions <= 0 && interactive == NO)
+ call error (0, "The regions list is empty.")
+ if (reflist != NULL) {
+ if (tfd != NULL)
+ call close (tfd)
+ tfd = rg_gxtransform (reflist, xc, Memc[str])
+ call rg_xsets (xc, REFFILE, Memc[str])
+ }
+ }
+ } else
+ call rg_xsets (xc, REFIMAGE, "reference")
+
+ # Open the input image.
+ im1 = immap (Memc[image1], READ_ONLY, 0)
+ if (IM_NDIM(im1) > 2) {
+ call error (0, "Input images must be 1D or 2D")
+ } else if (imr != NULL) {
+ if (IM_NDIM(im1) != IM_NDIM(imr))
+ call error (0,
+ "Input images must have same dimensionality as reference images")
+ }
+ call imseti (im1, IM_TYBNDRY, BT_NEAREST)
+ if (IM_NDIM(im1) == 1)
+ call imseti (im1, IM_NBNDRYPIX, IM_LEN(im1,1))
+ else
+ call imseti (im1, IM_NBNDRYPIX,
+ max (IM_LEN(im1,1), IM_LEN(im1,2)))
+ call rg_xsets (xc, IMAGE, Memc[image1])
+
+ # Open the output image if any.
+ if (list2 == NULL) {
+ im2 = NULL
+ Memc[image2] = EOS
+ } else if (imtgetim (list2, Memc[image2], SZ_FNAME) != EOF) {
+ call xt_mkimtemp (Memc[image1], Memc[image2], Memc[imtemp],
+ SZ_FNAME)
+ im2 = immap (Memc[image2], NEW_COPY, im1)
+ } else {
+ im2 = NULL
+ Memc[image2] = EOS
+ }
+ call rg_xsets (xc, OUTIMAGE, Memc[image2])
+
+ # Get the image record name for the shifts database.
+ if (reclist == NULL)
+ call strcpy (Memc[image1], Memc[str], SZ_FNAME)
+ else if (fntgfnb (reclist, Memc[str], SZ_FNAME) == EOF)
+ call strcpy (Memc[image1], Memc[str], SZ_FNAME)
+ call rg_xsets (xc, RECORD, Memc[str])
+
+ # Compute the initial coordinate shift.
+ if (tfd != NULL)
+ call rg_xtransform (tfd, xc)
+
+ # Perform the cross correlation function.
+ if (interactive == YES) {
+ stat = rg_xicorr (imr, im1, im2, sdb, dformat, reglist, tfd,
+ xc, gd, id)
+ } else {
+ stat = rg_xcorr (imr, im1, sdb, dformat, xc)
+ if (verbose == YES) {
+ call rg_xstats (xc, REFIMAGE, Memc[str], SZ_LINE)
+ call printf (
+ "Average shift from %s to %s is %g %g pixels\n")
+ call pargstr (Memc[image1])
+ call pargstr (Memc[str])
+ call pargr (rg_xstatr (xc, TXSHIFT))
+ call pargr (rg_xstatr (xc, TYSHIFT))
+ }
+ }
+
+ # Compute the overlap region for the images.
+ call rg_overlap (im1, rg_xstatr (xc, TXSHIFT),
+ rg_xstatr (xc,TYSHIFT), c1, c2, l1, l2, ncols, nlines)
+
+ # Shift the image and update the wcs.
+ if (im2 != NULL && stat == NO) {
+ if (verbose == YES) {
+ call printf (
+ "\tShifting image %s to image %s ...\n")
+ call pargstr (Memc[image1])
+ call pargstr (Memc[imtemp])
+ }
+
+ call rg_xshiftim (im1, im2, rg_xstatr (xc, TXSHIFT),
+ rg_xstatr (xc, TYSHIFT), Memc[interpstr], boundary,
+ constant)
+ mw = mw_openim (im1)
+ shifts[1] = rg_xstatr (xc, TXSHIFT)
+ shifts[2] = rg_xstatr (xc, TYSHIFT)
+ call mw_shift (mw, shifts, 03B)
+ call mw_saveim (mw, im2)
+ call mw_close (mw)
+ }
+
+ # Close up the input and output images.
+ call imunmap (im1)
+ if (im2 != NULL) {
+ call imunmap (im2)
+ if (stat == YES)
+ call imdelete (Memc[image2])
+ else
+ call xt_delimtemp (Memc[image2], Memc[imtemp])
+ }
+
+ if (stat == YES)
+ break
+ call rg_xindefr (xc)
+ }
+
+ if (verbose == YES)
+ call rg_poverlap (c1, c2, l1, l2, ncols, nlines)
+
+ call rg_xfree (xc)
+
+ # Close up the lists.
+ if (imr != NULL)
+ call imunmap (imr)
+ call imtclose (list1)
+ if (listr != NULL)
+ call imtclose (listr)
+ if (reglist != NULL)
+ call fntclsb (reglist)
+ if (list2 != NULL)
+ call imtclose (list2)
+ if (tfd != NULL)
+ call close (tfd)
+ if (reflist != NULL)
+ call fntclsb (reflist)
+ if (reclist != NULL)
+ call fntclsb (reclist)
+ if (dformat == YES)
+ call dtunmap (sdb)
+ else
+ call close (sdb)
+
+ # Close up the graphics and display devices.
+ if (gd != NULL)
+ call gclose (gd)
+ if (id != NULL)
+ call gclose (id)
+
+ call sfree (sp)
+end
+
+
+# RG_OVERLAP -- Compute the overlap region of the list of images.
+
+procedure rg_overlap (im1, xshift, yshift, x1, x2, y1, y2, ncols, nlines)
+
+pointer im1 # pointer to the input image
+real xshift # the computed x shift of the input image
+real yshift # the computed y shift of the input image
+int x1, x2 # the input/output column limits
+int y1, y2 # the input/output line limits
+int ncols, nlines # the input/output size limits
+
+int ixlo, ixhi, iylo, iyhi
+real xlo, xhi, ylo, yhi
+
+begin
+ if (IS_INDEFR(xshift) || IS_INDEFR(yshift))
+ return
+
+ # Compute the limits of the shifted image.
+ xlo = 1.0 + xshift
+ xhi = IM_LEN(im1,1) + xshift
+ ylo = 1.0 + yshift
+ yhi = IM_LEN(im1,2) + yshift
+
+ # Round up or down as appropriate.
+ ixlo = int (xlo)
+ if (xlo > ixlo)
+ ixlo = ixlo + 1
+ ixhi = int (xhi)
+ if (xhi < ixhi)
+ ixhi = ixhi - 1
+ iylo = int (ylo)
+ if (ylo > iylo)
+ iylo = iylo + 1
+ iyhi = int (yhi)
+ if (yhi < iyhi)
+ iyhi = iyhi - 1
+
+ # Determine the new limits.
+ if (IS_INDEFI(x1))
+ x1 = ixlo
+ else
+ x1 = max (ixlo, x1)
+ if (IS_INDEFI(x2))
+ x2 = ixhi
+ else
+ x2 = min (ixhi, x2)
+ if (IS_INDEFI(y1))
+ y1 = iylo
+ else
+ y1 = max (iylo, y1)
+ if (IS_INDEFI(y2))
+ y2 = iyhi
+ else
+ y2 = min (iyhi, y2)
+ if (IS_INDEFI(ncols))
+ ncols = IM_LEN(im1,1)
+ else
+ ncols = min (ncols, IM_LEN(im1,1))
+ if (IS_INDEFI(nlines))
+ nlines = IM_LEN(im1,2)
+ else
+ nlines = min (nlines, IM_LEN(im1,2))
+end
+
+
+# RG_POVERLAP -- Procedure to print the overlap and/or vignetted region.
+
+procedure rg_poverlap (x1, x2, y1, y2, ncols, nlines)
+
+int x1, x2 # the input column limits
+int y1, y2 # the input line limits
+int ncols, nlines # the number of lines and columns
+
+int vx1, vx2, vy1, vy2
+
+begin
+ vx1 = max (1, min (x1, ncols))
+ vx2 = max (1, min (x2, ncols))
+ vy1 = max (1, min (y1, nlines))
+ vy2 = max (1, min (y2, nlines))
+
+ call printf ("Overlap region: [%d:%d,%d:%d]\n")
+ call pargi (x1)
+ call pargi (x2)
+ call pargi (y1)
+ call pargi (y2)
+ if (vx1 != x1 || vx2 != x2 || vy1 != y1 || vy2 != y2) {
+ call printf ("Vignetted overlap region: [%d:%d,%d:%d]\n")
+ call pargi (vx1)
+ call pargi (vx2)
+ call pargi (vy1)
+ call pargi (vy2)
+ }
+end
diff --git a/pkg/images/immatch/src/xregister/xregister.h b/pkg/images/immatch/src/xregister/xregister.h
new file mode 100644
index 00000000..16c88b1e
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/xregister.h
@@ -0,0 +1,250 @@
+# Header file for XREGISTER
+
+# Define the cross correlation structure
+
+define LEN_XCSTRUCT (50 + 12 * SZ_FNAME + 12)
+
+define XC_RC1 Memi[$1] # pointers to 1st column of ref regions
+define XC_RC2 Memi[$1+1] # pointers to 2nd column of ref regions
+define XC_RL1 Memi[$1+2] # pointers to 1st line of ref regions
+define XC_RL2 Memi[$1+3] # pointers to 2nd line of ref regions
+define XC_RZERO Memi[$1+4] # pointers to zero pts of ref regions
+define XC_RXSLOPE Memi[$1+5] # pointers to x slopes of ref regions
+define XC_RYSLOPE Memi[$1+6] # pointers to y slopes of ref regions
+define XC_XSHIFTS Memi[$1+7] # pointers to x shifts of ref regions
+define XC_YSHIFTS Memi[$1+8] # pointers to y shifts of ref regions
+define XC_NREGIONS Memi[$1+9] # total number of regions
+define XC_CREGION Memi[$1+10] # the current region
+
+define XC_NREFPTS Memi[$1+11] # number of reference points
+define XC_XREF Memi[$1+12] # pointer to x reference points
+define XC_YREF Memi[$1+13] # pointer to y reference points
+define XC_TRANSFORM Memi[$1+14] # pointer to the transform
+define XC_IXLAG Memi[$1+15] # initial shift in x
+define XC_IYLAG Memi[$1+16] # initial shift in y
+define XC_XLAG Memi[$1+17] # current shift in x
+define XC_YLAG Memi[$1+18] # current shift in y
+define XC_DXLAG Memi[$1+19] # incremental shift in x
+define XC_DYLAG Memi[$1+20] # incremental shift in y
+
+define XC_BACKGRD Memi[$1+21] # type of background subtraction
+define XC_BORDER Memi[$1+22] # width of background border
+define XC_BVALUER Memr[P2R($1+23)] # reference background value
+define XC_BVALUE Memr[P2R($1+24)] # image bacground value
+define XC_LOREJECT Memr[P2R($1+25)] # low side rejection
+define XC_HIREJECT Memr[P2R($1+26)] # high side rejection
+define XC_APODIZE Memr[P2R($1+27)] # fraction of apodized region
+define XC_FILTER Memi[$1+28] # filter type
+
+define XC_CFUNC Memi[$1+30] # crosscor function
+define XC_XWINDOW Memi[$1+31] # width of correlation window in x
+define XC_YWINDOW Memi[$1+32] # width of correlation window in y
+define XC_XCOR Memi[$1+33] # pointer to cross-correlation function
+
+define XC_PFUNC Memi[$1+34] # correlation peak fitting function
+define XC_XCBOX Memi[$1+35] # x width of cor fitting box
+define XC_YCBOX Memi[$1+36] # y width of cor fitting box
+
+define XC_TXSHIFT Memr[P2R($1+37)] # total x shift
+define XC_TYSHIFT Memr[P2R($1+38)] # total y shift
+
+define XC_BSTRING Memc[P2C($1+50)] # background type
+define XC_FSTRING Memc[P2C($1+50+SZ_FNAME+1)] # filter string
+define XC_CSTRING Memc[P2C($1+50+2*SZ_FNAME+2)] # cross-correlation type
+define XC_PSTRING Memc[P2C($1+50+3*SZ_FNAME+3)] # peak centering
+
+define XC_IMAGE Memc[P2C($1+50+4*SZ_FNAME+4)] # input image
+define XC_REFIMAGE Memc[P2C($1+50+5*SZ_FNAME+5)] # reference image
+define XC_REGIONS Memc[P2C($1+50+6*SZ_FNAME+6)] # regions list
+define XC_DATABASE Memc[P2C($1+50+7*SZ_FNAME+7)] # shifts database
+define XC_OUTIMAGE Memc[P2C($1+50+8*SZ_FNAME+8)] # output image
+define XC_REFFILE Memc[P2C($1+50+9*SZ_FNAME+9)] # coordinates file
+define XC_RECORD Memc[P2C($1+50+10*SZ_FNAME+10)] # record
+
+# Define the id strings
+
+define RC1 1
+define RC2 2
+define RL1 3
+define RL2 4
+define RZERO 5
+define RXSLOPE 6
+define RYSLOPE 7
+define XSHIFTS 8
+define YSHIFTS 9
+define NREGIONS 10
+define CREGION 11
+
+define NREFPTS 12
+define XREF 13
+define YREF 14
+define TRANSFORM 15
+define IXLAG 16
+define IYLAG 17
+define XLAG 18
+define YLAG 19
+define DXLAG 20
+define DYLAG 21
+
+define BACKGRD 22
+define BVALUER 23
+define BVALUE 24
+define BORDER 25
+define LOREJECT 26
+define HIREJECT 27
+define APODIZE 28
+define FILTER 29
+
+define CFUNC 30
+define XWINDOW 31
+define YWINDOW 32
+define XCOR 33
+
+define PFUNC 34
+define XCBOX 35
+define YCBOX 36
+
+define TXSHIFT 37
+define TYSHIFT 38
+
+define CSTRING 39
+define BSTRING 40
+define PSTRING 41
+define FSTRING 42
+
+define IMAGE 43
+define REFIMAGE 44
+define REGIONS 45
+define OUTIMAGE 46
+define REFFILE 47
+define DATABASE 48
+define RECORD 49
+
+# Define the default parameter values
+
+define DEF_IXLAG 0
+define DEF_IYLAG 0
+define DEF_DXLAG 0
+define DEF_DYLAG 0
+define DEF_XWINDOW 5
+define DEF_YWINDOW 5
+
+define DEF_BACKGRD XC_BNONE
+define DEF_BORDER INDEFI
+define DEF_LOREJECT INDEFR
+define DEF_HIREJECT INDEFR
+
+define DEF_XCBOX 5
+define DEF_YCBOX 5
+define DEF_PFUNC XC_CENTROID
+
+# Define the background fitting techniques
+
+define XC_BNONE 1
+define XC_MEAN 2
+define XC_MEDIAN 3
+define XC_SLOPE 4
+
+define XC_BTYPES "|none|mean|median|plane|"
+
+# Define the filtering options
+
+define XC_FNONE 1
+define XC_LAPLACE 2
+
+define XC_FTYPES "|none|laplace|"
+
+# Define the cross correlation techniques
+
+define XC_DISCRETE 1
+define XC_FOURIER 2
+define XC_DIFFERENCE 3
+define XC_FILE 4
+
+define XC_CTYPES "|discrete|fourier|difference|file|"
+
+# Define the peak fitting functions
+
+define XC_PNONE 1
+define XC_CENTROID 2
+define XC_SAWTOOTH 3
+define XC_PARABOLA 4
+define XC_MARK 5
+
+define XC_PTYPES "|none|centroid|sawtooth|parabola|mark|"
+
+# Miscellaneous
+
+define MAX_NREGIONS 100
+define MAX_NREF 3
+define MAX_NTRANSFORM 6
+
+# Commands
+
+define XCMDS "|reference|input|regions|shifts|output|records|transform|\
+cregion|xlag|ylag|dxlag|dylag|background|border|loreject|hireject|apodize|\
+filter|correlation|xwindow|ywindow|function|xcbox|ycbox|show|mark|"
+
+define XSHOW "|data|background|correlation|center|"
+
+define XSHOW_DATA 1
+define XSHOW_BACKGROUND 2
+define XSHOW_CORRELATION 3
+define XSHOW_PEAKCENTER 4
+
+define XCMD_REFIMAGE 1
+define XCMD_IMAGE 2
+define XCMD_REGIONS 3
+define XCMD_DATABASE 4
+define XCMD_OUTIMAGE 5
+define XCMD_RECORD 6
+define XCMD_REFFILE 7
+define XCMD_CREGION 8
+define XCMD_XLAG 9
+define XCMD_YLAG 10
+define XCMD_DXLAG 11
+define XCMD_DYLAG 12
+define XCMD_BACKGROUND 13
+define XCMD_BORDER 14
+define XCMD_LOREJECT 15
+define XCMD_HIREJECT 16
+define XCMD_APODIZE 17
+define XCMD_FILTER 18
+define XCMD_CORRELATION 19
+define XCMD_XWINDOW 20
+define XCMD_YWINDOW 21
+define XCMD_PEAKCENTER 22
+define XCMD_XCBOX 23
+define XCMD_YCBOX 24
+define XCMD_SHOW 25
+define XCMD_MARK 26
+
+# Keywords
+
+define KY_REFIMAGE "reference"
+define KY_IMAGE "input"
+define KY_REGIONS "regions"
+define KY_DATABASE "shifts"
+define KY_OUTIMAGE "output"
+define KY_RECORD "record"
+define KY_REFFILE "coords"
+define KY_NREGIONS "nregions"
+define KY_CREGION "region"
+define KY_XLAG "xlag"
+define KY_YLAG "ylag"
+define KY_DXLAG "dxlag"
+define KY_DYLAG "dylag"
+define KY_BACKGROUND "background"
+define KY_BORDER "border"
+define KY_LOREJECT "loreject"
+define KY_HIREJECT "hireject"
+define KY_APODIZE "apodize"
+define KY_FILTER "filter"
+define KY_CORRELATION "correlation"
+define KY_XWINDOW "xwindow"
+define KY_YWINDOW "ywindow"
+define KY_PEAKCENTER "function"
+define KY_XCBOX "xcbox"
+define KY_YCBOX "ycbox"
+define KY_TXSHIFT "xshift"
+define KY_TYSHIFT "yshift"
diff --git a/pkg/images/immatch/src/xregister/xregister.key b/pkg/images/immatch/src/xregister/xregister.key
new file mode 100644
index 00000000..1956c88f
--- /dev/null
+++ b/pkg/images/immatch/src/xregister/xregister.key
@@ -0,0 +1,47 @@
+ Interactive Keystroke Commands
+
+? Print help
+: Colon commands
+t Define the offset between the reference and input images
+c Draw a contour plot of the cross-correlation function
+x Draw a column plot of the cross-correlation function
+y Draw a line plot of the cross-correlation function
+r Redraw the current plot
+f Recompute the cross-correlation function
+o Enter the image overlay plot submenu
+w Update the task parameters
+q Exit
+
+
+ Colon Commands
+
+:mark Mark regions on the display
+:show Show current values of all the parameters
+
+
+ Show/set Parameters
+
+:reference [string] Show/set the current reference image name
+:input [string] Show/set the current input image name
+:regions [string] Show/set the regions to be cross-correlated
+:shifts {string] Show/set the shifts database file name
+:coords [string] Show/set the current coordinates file name
+:output [string] Show/set the current output image name
+:records [string] Show/set the current database record name
+:xlag [value] Show/set the initial lag in x
+:ylag [value] Show/set the initial lag in y
+:dxlag [value] Show/set the incremental lag in x
+:dylag [value] Show/set the incremental lag in y
+:cregion [value] Show/set the current region
+:background [string] Show/set the background fitting function
+:border [value] Show/set border region for background fitting
+:loreject [value] Show/set low side k-sigma rejection parameter
+:hireject [value] Show/set high side k-sigma rejection parameter
+:apodize [value] Show/set percent of end points to apodize
+:filter [string] Show/set the default spatial filter
+:correlation [string] Show/set the cross-correlation function
+:xwindow [value] Show/set width of cross-correlation window in x
+:ywindow [value] Show/set width of cross-correlation window in y
+:function [string] Show/set correlation peak centering function
+:xcbox [value] Show/set the centering box width in x
+:ycbox [value] Show/set the centering box width in y
diff --git a/pkg/images/immatch/sregister.cl b/pkg/images/immatch/sregister.cl
new file mode 100644
index 00000000..38dc84ad
--- /dev/null
+++ b/pkg/images/immatch/sregister.cl
@@ -0,0 +1,151 @@
+# SREGISTER -- Compute the geometric transformation required to register an
+# input image to a reference image using celestial coordinate WCS information
+# in the input and reference image headers, and perform the registration.
+# SREGISTER is a simple script task which calls the SKYXYMATCH task to compute
+# the control points, the GEOMAP task to compute the transformation, and the
+# GEOTRAN task to do the registration.
+
+procedure sregister (input, reference, output)
+
+file input {prompt="The input images"}
+file reference {prompt="Input reference images"}
+file output {prompt="The output registered images"}
+real xmin {INDEF,
+ prompt="Minimum logical x reference coordinate value"}
+real xmax {INDEF,
+ prompt="Maximum logical x reference coordinate value"}
+real ymin {INDEF,
+ prompt="Minimum logical y reference coordinate value"}
+real ymax {INDEF,
+ prompt="Maximum logical y reference coordinate value"}
+int nx {10, prompt="Number of grid points in x"}
+int ny {10, prompt="Number of grid points in y"}
+string wcs {"world", prompt="The default world coordinate system",
+ enum="physical|world"}
+string xformat {"%10.3f", prompt="Output logical x coordinate format"}
+string yformat {"%10.3f", prompt="Output logical y coordinate format"}
+string rwxformat {"",
+ prompt="Output reference world x coordinate format"}
+string rwyformat {"",
+ prompt="Output reference world y coordinate format"}
+string wxformat {"", prompt="Output world x coordinate format"}
+string wyformat {"", prompt="Output world y coordinate format"}
+
+string fitgeometry {"general",
+ prompt="Fitting geometry",
+ enum="shift|xyscale|rotate|rscale|rxyscale|general"}
+string function {"polynomial",
+ prompt="Type of coordinate surface to be computed",
+ enum="legendre|chebyshev|polynomial"}
+int xxorder {2, prompt="Order of x fit in x"}
+int xyorder {2, prompt="Order of x fit in y"}
+string xxterms {"half", enum="none|half|full",
+ prompt="X fit cross terms type"}
+int yxorder {2, prompt="Order of y fit in x"}
+int yyorder {2, prompt="Order of y fit in y"}
+string yxterms {"half", enum="none|half|full",
+ prompt="Y fit cross terms type"}
+real reject {INDEF, prompt="The rejection limit in units of sigma"}
+string calctype {"real", prompt="Transformation computation type",
+ enum="real|double"}
+
+string geometry {"geometric", prompt="Transformation geometry",
+ enum="linear|geometric"}
+real xsample {1.0,prompt="X coordinate sampling interval"}
+real ysample {1.0,prompt="Y coordinate sampling interval"}
+string interpolant {"linear", prompt="The interpolant type"}
+string boundary {"nearest", prompt="Boundary extensiontype",
+ enum="nearest|constant|reflect|wrap"}
+real constant {0.0, prompt="Constant for constant boundary extension"}
+bool fluxconserve {yes, prompt="Preserve image flux ?"}
+int nxblock {512, prompt="X dimension blocking factor"}
+int nyblock {512, prompt="Y dimension blocking factor"}
+
+bool wcsinherit {yes, prompt="Inherit wcs of the reference image ?"}
+
+bool verbose {yes, prompt="Print messages about progress of task?"}
+bool interactive {no, prompt="Compute transformation interactively? "}
+string graphics {"stdgraph", prompt="The standard graphics device"}
+gcur gcommands {"", prompt="The graphics cursor"}
+
+
+begin
+ # Declare local variables.
+ int nimages
+ string tinput, treference, tcoords, tcname, tdatabase, toutput
+ string tsections1, tsections2
+
+ # Get the query parameters.
+ tinput = input
+ treference = reference
+ toutput = output
+
+ # Cache the sections task.
+ cache sections
+
+ # Get the coordinates file list.
+ tsections1 = mktemp ("tmps1")
+ tsections2 = mktemp ("tmps2")
+ if (access ("imxymatch.1")) {
+ tcoords = mktemp ("imxymatch")
+ } else {
+ tcoords = "imxymatch"
+ }
+ sections (tinput, option="fullname", > tsections1)
+ nimages = sections.nimages
+ for (i = 1; i <= nimages; i = i + 1) {
+ printf ("%s\n", tcoords // "." // i, >> tsections2)
+ }
+ delete (tsections1, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+ tcname = "@"//tsections2
+
+ # Get the output database file name.
+ if (access ("sregister.db")) {
+ tdatabase = mktemp ("tmpdb")
+ } else {
+ tdatabase = "sregister.db"
+ }
+
+ # Compute the control points.
+ skyxymatch (tinput, treference, tcname, coords="grid", xmin=xmin,
+ xmax=xmax, ymin=ymin, ymax=ymax, nx=nx, ny=ny, wcs=wcs,
+ xcolumn=1, ycolumn=1, xunits="", yunits="", xformat=xformat,
+ yformat=yformat, rwxformat=rwxformat, rwyformat=rwyformat,
+ wxformat=wxformat, wyformat=wyformat, min_sigdigits=7, verbose=no)
+
+ # Compute the transformation.
+ geomap (tcname, tdatabase, xmin, xmax, ymin, ymax, transforms=tinput,
+ results="", fitgeometry=fitgeometry, function=function,
+ xxorder=xxorder, xyorder=xyorder, xxterms=xxterms, yxorder=yxorder,
+ yyorder=yyorder, yxterms=yxterms, reject=reject, calctype=calctype,
+ verbose=verbose, interactive=interactive, graphics=graphics,
+ cursor=gcommands)
+
+ # Register the images.
+ geotran (tinput, toutput, database=tdatabase, transforms=tinput,
+ geometry=geometry, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax,
+ xscale=1.0, yscale=1.0, ncols=INDEF, nlines=INDEF,
+ interpolant=interpolant, boundary=boundary, constant=constant,
+ fluxconserve=fluxconserve, xsample=xsample, ysample=ysample,
+ nxblock=nxblock, nyblock=nyblock, xin=INDEF, yin=INDEF, xout=INDEF,
+ yout=INDEF, xshift=INDEF, yshift=INDEF, xmag=INDEF, ymag=INDEF,
+ xrotation=INDEF, yrotation=INDEF, verbose=verbose)
+
+ # Copy the reference wcs to the input images.
+ if (wcsinherit) {
+ wcscopy (toutput, treference, verbose-)
+ }
+
+ # Delete the coordinates files.
+ delete (tcname, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+
+ # Delete the coordinates file list.
+ delete (tsections2, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+
+ # Delete the database file.
+ delete (tdatabase, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+end
diff --git a/pkg/images/immatch/wcscopy.par b/pkg/images/immatch/wcscopy.par
new file mode 100644
index 00000000..a5bff29c
--- /dev/null
+++ b/pkg/images/immatch/wcscopy.par
@@ -0,0 +1,5 @@
+# Parameter file for WCSCOPY
+
+images,f,a,,,,"List of input images"
+refimages,f,a,,,,"List of reference images"
+verbose,b,h,yes,,,"Print messages about actions taken ?"
diff --git a/pkg/images/immatch/wcsmap.cl b/pkg/images/immatch/wcsmap.cl
new file mode 100644
index 00000000..2a052dc0
--- /dev/null
+++ b/pkg/images/immatch/wcsmap.cl
@@ -0,0 +1,111 @@
+# WCSMAP -- Compute the geometric transformation required to register an
+# input image to a reference image using WCS information in the input and
+# reference image headers. WCSMAP is a simple script task which calls the
+# WCSXYMATCH task to compute the control points followed by the GEOMAP
+# task to compute the transformation.
+
+
+procedure wcsmap (input, reference, database)
+
+file input {prompt="The input images"}
+file reference {prompt="The input reference images"}
+file database {prompt="The output database file"}
+string transforms {"", prompt="The database transform names"}
+string results {"", prompt="The optional results summary files"}
+real xmin {INDEF,
+ prompt="Minimum logical x reference coordinate value"}
+real xmax {INDEF,
+ prompt="Maximum logical x reference coordinate value"}
+real ymin {INDEF,
+ prompt="Minimum logical y reference coordinate value"}
+real ymax {INDEF,
+ prompt="Maximum logical y reference coordinate value"}
+int nx {10, prompt="Number of grid points in x"}
+int ny {10, prompt="Number of grid points in y"}
+string wcs {"world", prompt="The default world coordinate system",
+ enum="physical|world"}
+bool transpose {no, prompt="Force a world coordinate tranpose ?"}
+string xformat {"%10.3f", prompt="Output logical x coordinate format"}
+string yformat {"%10.3f", prompt="Output logical y coordinate format"}
+string wxformat {"", prompt="Output world x coordinate format"}
+string wyformat {"", prompt="Output world y coordinate format"}
+string fitgeometry {"general",
+ prompt="Fitting geometry",
+ enum="shift|xyscale|rotate|rscale|rxyscale|general"}
+string function {"polynomial", prompt="Surface type",
+ enum="legendre|chebyshev|polynomial"}
+int xxorder {2, prompt="Order of x fit in x"}
+int xyorder {2, prompt="Order of x fit in y"}
+string xxterms {"half", enum="none|half|full",
+ prompt="X fit cross terms type"}
+int yxorder {2, prompt="Order of y fit in x"}
+int yyorder {2, prompt="Order of y fit in y"}
+string yxterms {"half", enum="none|half|full",
+ prompt="Y fit cross terms type"}
+real reject {INDEF, prompt="Rejection limit in sigma units"}
+string calctype {"real", prompt="Computation precision",
+ enum="real|double"}
+bool verbose {yes, prompt="Print messages about progress of task ?"}
+bool interactive {yes, prompt="Compute transformation interactively ? "}
+string graphics {"stdgraph", prompt="Default graphics device"}
+gcur gcommands {"", prompt="Graphics cursor"}
+
+
+begin
+ # Declare local variables.
+ int nimages
+ string tinput, treference, toutput, ttransforms, tresults, tcoords
+ string tsections1, tsections2, tcname
+
+ # Cache the sections task.
+ cache sections
+
+ # Get the query parameters.
+ tinput = input
+ treference = reference
+ toutput = database
+ if (transforms == "") {
+ ttransforms = tinput
+ } else {
+ ttransforms = transforms
+ }
+ tresults = results
+
+ # Get the temporary coordinates file list.
+ tsections1 = mktemp ("tmps1")
+ tsections2 = mktemp ("tmps2")
+ if (access ("imxymatch.1")) {
+ tcoords = mktemp ("imxymatch")
+ } else {
+ tcoords = "imxymatch"
+ }
+ sections (tinput, option="fullname", > tsections1)
+ nimages = sections.nimages
+ for (i = 1; i <= nimages; i = i + 1) {
+ printf ("%s\n", tcoords // "." // i, >> tsections2)
+ }
+ delete (tsections1, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+ tcname = "@"//tsections2
+
+ # Compute the control points.
+ wcsxymatch (tinput, treference, tcname, coords="grid", xmin=xmin,
+ xmax=xmax, ymin=ymin, ymax=ymax, nx=nx, ny=ny, wcs=wcs,
+ transpose=transpose, xcolumn=1, ycolumn=1, xunits="", yunits="",
+ xformat=xformat, yformat=yformat, wxformat=wxformat,
+ wyformat=wyformat, min_sigdigits=7, verbose=no)
+
+ # Compute the transformation.
+ geomap (tcname, toutput, xmin, xmax, ymin, ymax, transforms=ttransforms,
+ results = tresults, fitgeometry=fitgeometry, function=function,
+ xxorder=xxorder, xyorder=xyorder, xxterms=xxterms, yxorder=yxorder,
+ yyorder=yyorder, yxterms=yxterms, reject=reject, calctype=calctype,
+ verbose=verbose, interactive=interactive, graphics=graphics,
+ cursor=gcommands)
+
+ # Cleanup.
+ delete (tcname, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+ delete (tsections2, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+end
diff --git a/pkg/images/immatch/wcsxymatch.par b/pkg/images/immatch/wcsxymatch.par
new file mode 100644
index 00000000..b7bab19e
--- /dev/null
+++ b/pkg/images/immatch/wcsxymatch.par
@@ -0,0 +1,25 @@
+# Parameter file for the WCSXYMATCH task
+
+input,f,a,,,,Input images
+reference,f,a,,,,Input reference images
+output,f,a,,,,Output matched coordinate lists
+coords,f,h,"grid",,,Reference coordinate lists
+xmin,r,h,INDEF,,,Minimum logical x reference coordinate value
+xmax,r,h,INDEF,,,Maximum logical x reference coordinate value
+ymin,r,h,INDEF,,,Minimum logical y reference coordinate value
+ymax,r,h,INDEF,,,Maximum logical y reference coordinate value
+nx,i,h,10,1,,Number of grid points in x
+ny,i,h,10,1,,Number of grid points in y
+wcs,s,h,"world","|physical|world|",,Input coordinate system
+transpose,b,h,no,,,Force a world coordinate transpose ?
+xcolumn,i,h,1,1,,Input column containing x coordinate
+ycolumn,i,h,2,1,,Input column containing y coordinate
+xunits,s,h,"",,,Input x coordinate units
+yunits,s,h,"",,,Input y coordinate units
+xformat,s,h,"%10.3f",,,Output logical x coordinate format
+yformat,s,h,"%10.3f",,,Output logical y coordinate format
+wxformat,s,h,"",,,Output world x coordinate format
+wyformat,s,h,"",,,Output world y coordinate format
+min_sigdigits,i,h,7,,,Minimum number of significant digits
+verbose,b,h,yes,,,Verbose mode ?
+mode,s,h,ql,,,
diff --git a/pkg/images/immatch/wregister.cl b/pkg/images/immatch/wregister.cl
new file mode 100644
index 00000000..0817eeac
--- /dev/null
+++ b/pkg/images/immatch/wregister.cl
@@ -0,0 +1,148 @@
+# WREGISTER -- Compute the geometric transformation required to register an
+# input image to a reference image using WCS information in the input and
+# reference image headers, and perform the registration. WREGISTER is a simple
+# script task which calls the WCSXYMATCH task to compute the control points,
+# the GEOMAP task to compute the transformation, and the GEOTRAN task to do
+# the registration.
+
+procedure wregister (input, reference, output)
+
+file input {prompt="The input images"}
+file reference {prompt="Input reference images"}
+file output {prompt="The output registered images"}
+real xmin {INDEF,
+ prompt="Minimum logical x reference coordinate value"}
+real xmax {INDEF,
+ prompt="Maximum logical x reference coordinate value"}
+real ymin {INDEF,
+ prompt="Minimum logical y reference coordinate value"}
+real ymax {INDEF,
+ prompt="Maximum logical y reference coordinate value"}
+int nx {10, prompt="Number of grid points in x"}
+int ny {10, prompt="Number of grid points in y"}
+string wcs {"world", prompt="The default world coordinate system",
+ enum="physical|world"}
+bool transpose {no, prompt="Force a world coordinate tranpose ?"}
+string xformat {"%10.3f", prompt="Output logical x coordinate format"}
+string yformat {"%10.3f", prompt="Output logical y coordinate format"}
+string wxformat {"", prompt="Output world x coordinate format"}
+string wyformat {"", prompt="Output world y coordinate format"}
+
+string fitgeometry {"general",
+ prompt="Fitting geometry",
+ enum="shift|xyscale|rotate|rscale|rxyscale|general"}
+string function {"polynomial",
+ prompt="Type of coordinate surface to be computed",
+ enum="legendre|chebyshev|polynomial"}
+int xxorder {2, prompt="Order of x fit in x"}
+int xyorder {2, prompt="Order of x fit in y"}
+string xxterms {"half", enum="none|half|full",
+ prompt="X fit cross terms type"}
+int yxorder {2, prompt="Order of y fit in x"}
+int yyorder {2, prompt="Order of y fit in y"}
+string yxterms {"half", enum="none|half|full",
+ prompt="Y fit cross terms type"}
+real reject {INDEF, prompt="The rejection limit in units of sigma"}
+string calctype {"real", prompt="Transformation computation type",
+ enum="real|double"}
+
+string geometry {"geometric", prompt="Transformation geometry",
+ enum="linear|geometric"}
+real xsample {1.0,prompt="X coordinate sampling interval"}
+real ysample {1.0,prompt="Y coordinate sampling interval"}
+string interpolant {"linear", prompt="The interpolant type"}
+string boundary {"nearest", prompt="Boundary extensiontype",
+ enum="nearest|constant|reflect|wrap"}
+real constant {0.0, prompt="Constant for constant boundary extension"}
+bool fluxconserve {yes, prompt="Preserve image flux ?"}
+int nxblock {512, prompt="X dimension blocking factor"}
+int nyblock {512, prompt="Y dimension blocking factor"}
+
+bool wcsinherit {yes, prompt="Inherit wcs of the reference image ?"}
+
+bool verbose {yes, prompt="Print messages about progress of task?"}
+bool interactive {no, prompt="Compute transformation interactively? "}
+string graphics {"stdgraph", prompt="The standard graphics device"}
+gcur gcommands {"", prompt="The graphics cursor"}
+
+
+begin
+ # Declare local variables.
+ int nimages
+ string tinput, treference, tcoords, tcname, tdatabase, toutput
+ string tsections1, tsections2
+
+ # Get the query parameters.
+ tinput = input
+ treference = reference
+ toutput = output
+
+ # Cache the sections task.
+ cache sections
+
+ # Get the coordinates file list.
+ tsections1 = mktemp ("tmps1")
+ tsections2 = mktemp ("tmps2")
+ if (access ("imxymatch.1")) {
+ tcoords = mktemp ("imxymatch")
+ } else {
+ tcoords = "imxymatch"
+ }
+ sections (tinput, option="fullname", > tsections1)
+ nimages = sections.nimages
+ for (i = 1; i <= nimages; i = i + 1) {
+ printf ("%s\n", tcoords // "." // i, >> tsections2)
+ }
+ delete (tsections1, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+ tcname = "@"//tsections2
+
+ # Get the output database file name.
+ if (access ("wregister.db")) {
+ tdatabase = mktemp ("tmpdb")
+ } else {
+ tdatabase = "wregister.db"
+ }
+
+ # Compute the control points.
+ wcsxymatch (tinput, treference, tcname, coords="grid", xmin=xmin,
+ xmax=xmax, ymin=ymin, ymax=ymax, nx=nx, ny=ny, wcs=wcs,
+ transpose=transpose, xcolumn=1, ycolumn=1, xunits="", yunits="",
+ xformat=xformat, yformat=yformat, wxformat=wxformat,
+ wyformat=wyformat, min_sigdigits=7, verbose=no)
+
+ # Compute the transformation.
+ geomap (tcname, tdatabase, xmin, xmax, ymin, ymax, transforms=tinput,
+ results="", fitgeometry=fitgeometry, function=function,
+ xxorder=xxorder, xyorder=xyorder, xxterms=xxterms, yxorder=yxorder,
+ yyorder=yyorder, yxterms=yxterms, reject=reject, calctype=calctype,
+ verbose=verbose, interactive=interactive, graphics=graphics,
+ cursor=gcommands)
+
+ # Register the images.
+ geotran (tinput, toutput, database=tdatabase, transforms=tinput,
+ geometry=geometry, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax,
+ xscale=1.0, yscale=1.0, ncols=INDEF, nlines=INDEF,
+ interpolant=interpolant, boundary=boundary, constant=constant,
+ fluxconserve=fluxconserve, xsample=xsample, ysample=ysample,
+ nxblock=nxblock, nyblock=nyblock, xin=INDEF, yin=INDEF, xout=INDEF,
+ yout=INDEF, xshift=INDEF, yshift=INDEF, xmag=INDEF, ymag=INDEF,
+ xrotation=INDEF, yrotation=INDEF, verbose=verbose)
+
+ # Copy the reference wcs to the input images.
+ if (wcsinherit) {
+ wcscopy (toutput, treference, verbose-)
+ }
+
+ # Delete the coordinates files.
+ delete (tcname, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+
+ # Delete the coordinates file list.
+ delete (tsections2, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+
+ # Delete the database file.
+ delete (tdatabase, go_ahead+, verify-, default_action+,
+ allversions+, subfiles+, > "dev$null")
+end
diff --git a/pkg/images/immatch/xregister.par b/pkg/images/immatch/xregister.par
new file mode 100644
index 00000000..6e2da00b
--- /dev/null
+++ b/pkg/images/immatch/xregister.par
@@ -0,0 +1,42 @@
+# Parameter file for the XREGISTER task
+
+input,s,a,,,,Input images to be registered
+reference,s,a,,,,Input reference images
+regions,s,a,"",,,Reference image regions used for registration
+shifts,f,a,"",,,Input/output shifts database file
+output,s,h,"",,,Output registered images
+databasefmt,b,h,yes,,,Write the shifts file in database format ?
+append,b,h,no,,,Open shifts database for writing in append mode ?
+records,s,h,"",,,List of shifts database records
+coords,f,h,"",,,Input coordinate files defining the initial shifts
+xlag,i,h,0,,,Initial shift in x
+ylag,i,h,0,,,Initial shift in y
+dxlag,i,h,0,,,Incremental shift in x
+dylag,i,h,0,,,Incremental shift in y
+
+background,s,h,"none","|none|mean|median|plane|",,Background fitting function
+border,i,h,INDEF,,,Width of border for background fitting
+loreject,r,h,INDEF,,,Low side k-sigma rejection factor
+hireject,r,h,INDEF,,,High side k-sigma rejection factor
+apodize,r,h,0.0,0.0,0.5,Fraction of endpoints to apodize
+filter,s,h,"none","|none|laplace|",,Spatially filter the data
+
+correlation,s,h,"discrete","|discrete|fourier|difference|file|",,Cross-correlation function
+xwindow,i,h,11,3,,Width of correlation window in x
+ywindow,i,h,11,3,,Width of correlation window in y
+
+function,s,h,"centroid","|none|centroid|sawtooth|parabola|mark|",,Correlation peak centering function
+xcbox,i,h,5,3,,X box width for centering correlation peak
+ycbox,i,h,5,3,,Y box width for fitting correlation peak
+
+interp_type,s,h,"linear",,,'Interpolant'
+boundary_type,s,h,"nearest","|constant|nearest|reflect|wrap|",,'Boundary (constant,nearest,reflect,wrap)'
+constant,r,h,0.0,,,Constant for constant boundary extension
+
+interactive,b,h,no,,,Interactive mode ?
+verbose,b,h,yes,,,Verbose mode ?
+graphics,s,h,"stdgraph",,,The standard graphics device
+display,s,h,"stdimage",,,The standard image display device
+gcommands,*gcur,h,"",,,The graphics cursor
+icommands,*imcur,h,"",,,The image display cursor
+mode,s,h,ql,,,
diff --git a/pkg/images/immatch/xyxymatch.par b/pkg/images/immatch/xyxymatch.par
new file mode 100644
index 00000000..0a644e6d
--- /dev/null
+++ b/pkg/images/immatch/xyxymatch.par
@@ -0,0 +1,36 @@
+# Parameter file for XYXYMATCH
+
+input,f,a,,,,The input lists
+reference,f,a,,,,The reference lists
+output,f,a,,,,The output matched coordinate lists
+tolerance,r,a,3,,,The matching tolerance in pixels
+
+refpoints,f,h,"",,,Optional list of reference points
+xin,r,h,INDEF,,,X origin of input list
+yin,r,h,INDEF,,,Y origin of input list
+xmag,r,h,INDEF,,,X magnification required to match input to reference list
+ymag,r,h,INDEF,,,Y magnification required to match input to reference list
+xrotation,r,h,INDEF,,,X rotation required to match input to reference list
+yrotation,r,h,INDEF,,,Y rotation required to match input to reference list
+xref,r,h,INDEF,,,X origin of reference list
+yref,r,h,INDEF,,,Y origin of reference list
+
+xcolumn,i,h,1,,,Input list column containing the x coordinate
+ycolumn,i,h,2,,,Input list column containing the y coordinate
+xrcolumn,i,h,1,,,Reference list column containing the x coordinate
+yrcolumn,i,h,2,,,Reference list column containing the y coordinate
+
+separation,r,h,9.0,,,The minimum object separation
+matching,s,h,"triangles","|tolerance|triangles|",,The matching algorithm
+nmatch,i,h,30,,,The maximum number of points for triangles algorithm
+ratio,r,h,10.0,5.0,10.0,The maximum ratio of longest to shortest side of triangle
+nreject,i,h,10,,,The maximum number of rejection iterations
+
+xformat,s,h,"%13.3f",,,The format of the output x coordinate
+yformat,s,h,"%13.3f",,,The format of the output y coordinate
+
+interactive,b,h,no,,,Interactive mode ?
+verbose,b,h,yes,,,Verbose mode ?
+icommands,*imcur,h,"",,,The image display cursor
+
+mode,s,h,ql,,,