aboutsummaryrefslogtreecommitdiff
path: root/vendor/x11iraf/ximtool/zscale.c
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/x11iraf/ximtool/zscale.c')
-rw-r--r--vendor/x11iraf/ximtool/zscale.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/vendor/x11iraf/ximtool/zscale.c b/vendor/x11iraf/ximtool/zscale.c
new file mode 100644
index 00000000..b516a5ef
--- /dev/null
+++ b/vendor/x11iraf/ximtool/zscale.c
@@ -0,0 +1,532 @@
+#include <stdio.h>
+#include <math.h>
+
+/*
+ * ZSCALE -- Compute the optimal Z1, Z2 (range of greyscale values to be
+ * displayed) of an image. For efficiency a statistical subsample of an image
+ * is used. The pixel sample evenly subsamples the image in x and y. The
+ * entire image is used if the number of pixels in the image is smaller than
+ * the desired sample.
+ *
+ * The sample is accumulated in a buffer and sorted by greyscale value.
+ * The median value is the central value of the sorted array. The slope of a
+ * straight line fitted to the sorted sample is a measure of the standard
+ * deviation of the sample about the median value. Our algorithm is to sort
+ * the sample and perform an iterative fit of a straight line to the sample,
+ * using pixel rejection to omit gross deviants near the endpoints. The fitted
+ * straight line is the transfer function used to map image Z into display Z.
+ * If more than half the pixels are rejected the full range is used. The slope
+ * of the fitted line is divided by the user-supplied contrast factor and the
+ * final Z1 and Z2 are computed, taking the origin of the fitted line at the
+ * median value.
+ */
+
+#define MIN_NPIXELS 5 /* smallest permissible sample */
+#define MAX_REJECT 0.5 /* max frac. of pixels to be rejected */
+#define GOOD_PIXEL 0 /* use pixel in fit */
+#define BAD_PIXEL 1 /* ignore pixel in all computations */
+#define REJECT_PIXEL 2 /* reject pixel after a bit */
+#define KREJ 2.5 /* k-sigma pixel rejection factor */
+#define MAX_ITERATIONS 5 /* maximum number of fitline iterations */
+#define INDEF 0
+
+#define ZSC_DBG 0
+
+#undef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#undef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#undef mod
+#define mod(a,b) ((a) % (b))
+#undef nint
+#define nint(a) ((int)(a + 0.5))
+#undef abs
+#define abs(a) ((a) >= 0 ? (a) : -(a))
+
+
+static void flattenData(), subSample();
+static int sampleImage(), fitLine(), floatCompare();
+static int rejectPixels(), computeSigma();
+
+
+
+/* ZSCALE -- Sample the image and compute optimal Z1 and Z2 values.
+ */
+
+void
+zscale (im, nx, ny, bitpix, z1, z2, contrast, opt_size, len_stdline)
+
+char *im; /* image data to be sampled */
+int nx, ny; /* image dimensions */
+int bitpix; /* bits per pixel */
+float *z1, *z2; /* output min and max greyscale values */
+float contrast; /* adj. to slope of transfer function */
+int opt_size; /* desired number of pixels in sample */
+int len_stdline; /* optimal number of pixels per line */
+{
+ register int npix, minpix, ngoodpix, center_pixel, ngrow;
+ float zmin, zmax, median;
+ float zstart, zslope;
+ float *sample, *left;
+
+ /* Subsample the image. */
+ npix = sampleImage(im, bitpix, &sample, nx, ny, opt_size, len_stdline);
+
+ /* Sort the sample, compute the minimum, maximum, and median pixel
+ * values.
+ */
+ qsort (sample, npix, sizeof (float), floatCompare);
+ zmin = *sample;
+ zmax = *(sample+npix-1);
+
+ /* The median value is the average of the two central values if there
+ * are an even number of pixels in the sample.
+ */
+ center_pixel = max (1, (npix + 1) / 2);
+ left = &(sample[center_pixel - 1]);
+ if (mod (npix, 2) == 1 || center_pixel >= npix)
+ median = *left;
+ else
+ median = (*left + *(left+1)) / 2;
+
+ /* Fit a line to the sorted sample vector. If more than half of the
+ * pixels in the sample are rejected give up and return the full range.
+ * If the user-supplied contrast factor is not 1.0 adjust the scale
+ * accordingly and compute Z1 and Z2, the y intercepts at indices 1 and
+ * npix.
+ */
+ minpix = max (MIN_NPIXELS, (int) (npix * MAX_REJECT));
+ ngrow = max (1, nint (npix * .01));
+ ngoodpix = fitLine (sample, npix, &zstart, &zslope,
+ KREJ, ngrow, MAX_ITERATIONS);
+
+ if (ngoodpix < minpix) {
+ *z1 = zmin;
+ *z2 = zmax;
+ } else {
+ if (contrast > 0)
+ zslope = zslope / contrast;
+ *z1 = max (zmin, median - (center_pixel - 1) * zslope);
+ *z2 = min (zmax, median + (npix - center_pixel) * zslope);
+ }
+
+ if (ZSC_DBG) {
+ printf ("\tzmin=%g zmax=%g\tleft=%g median=%g\n",
+ zmin, zmax, *left, median);
+ printf ("\tminpix=%d ngrow=%d ngood=%d\n", minpix,ngrow,ngoodpix);
+ printf ("\tzstart=%g zslope=%g\n\tz1=%g z2=%g\n",
+ zstart, zslope, *z1, *z2);
+ }
+
+ /* Clean up. */
+ free ((float *)sample);
+}
+
+
+/* sampleImage -- Extract an evenly gridded subsample of the pixels from
+ * a two-dimensional image into a one-dimensional vector.
+ */
+
+static int
+sampleImage (im, bitpix, sample, nx, ny, optimal_size, len_stdline)
+
+char *im; /* image to be sampled */
+int bitpix; /* bits per pixel in image */
+float **sample; /* output vector containing the sample */
+int nx, ny; /* image dimensions */
+int optimal_size; /* desired number of pixels in sample */
+int len_stdline; /* optimal number of pixels per line */
+{
+ register int i;
+ int ncols, nlines, col_step, line_step, maxpix, line;
+ int opt_npix_per_line, npix_per_line, npix = 0;
+ int opt_nlines_in_sample, min_nlines_in_sample, max_nlines_in_sample;
+ float *op, *row;
+ int *ipix;
+ float *fpix;
+ double *dpix;
+ short *spix;
+ char *bpix;
+
+
+ ncols = nx;
+ nlines = ny;
+
+ /* Compute the number of pixels each line will contribute to the sample,
+ * and the subsampling step size for a line. The sampling grid must
+ * span the whole line on a uniform grid.
+ */
+ opt_npix_per_line = max (1, min (ncols, len_stdline));
+ col_step = max (2, (ncols + opt_npix_per_line-1) / opt_npix_per_line);
+ npix_per_line = max (1, (ncols + col_step-1) / col_step);
+
+ /* Compute the number of lines to sample and the spacing between lines.
+ * We must ensure that the image is adequately sampled despite its
+ * size, hence there is a lower limit on the number of lines in the
+ * sample. We also want to minimize the number of lines accessed when
+ * accessing a large image, because each disk seek and read is ex-
+ * pensive. The number of lines extracted will be roughly the sample
+ * size divided by len_stdline, possibly more if the lines are very
+ * short.
+ */
+ min_nlines_in_sample = max (1, optimal_size / len_stdline);
+ opt_nlines_in_sample = max(min_nlines_in_sample, min(nlines,
+ (optimal_size + npix_per_line-1) / npix_per_line));
+ line_step = max (2, nlines / (opt_nlines_in_sample));
+ max_nlines_in_sample = (nlines + line_step-1) / line_step;
+
+ /* Allocate space for the output vector. Buffer must be freed by our
+ * caller.
+ */
+ maxpix = npix_per_line * max_nlines_in_sample;
+ *sample = (float *) malloc (maxpix * sizeof (float));
+ row = (float *) malloc (nx * sizeof (float));
+
+ /* Extract the vector. */
+ op = *sample;
+ for (line = (line_step + 1)/2; line < nlines; line+=line_step) {
+ /* Load a row of float values from the image */
+ switch (bitpix) {
+ case 8:
+ bpix = (char *) &im[(line-1) * nx * sizeof(char)];
+ for (i=0; i < nx; i++)
+ row[i] = (float) bpix[i];
+ break;
+ case 16:
+ spix = (short *) &im[(line-1) * nx * sizeof(short)];
+ for (i=0; i < nx; i++)
+ row[i] = (float) spix[i];
+ break;
+ case 32:
+ ipix = (int *) &im[(line-1) * nx * sizeof(int)];
+ for (i=0; i < nx; i++)
+ row[i] = (float) ipix[i];
+ break;
+ case -32:
+ fpix = (float *) &im[(line-1) * nx * sizeof(float)];
+ for (i=0; i < nx; i++)
+ row[i] = (float) fpix[i];
+ break;
+ case -64:
+ dpix = (double *) &im[(line-1) * nx * sizeof(double)];
+ for (i=0; i < nx; i++)
+ row[i] = (float) dpix[i];
+ break;
+ }
+
+ subSample (row, op, npix_per_line, col_step);
+ op += npix_per_line;
+ npix += npix_per_line;
+ if (npix > maxpix)
+ break;
+ }
+
+ free ((float *)row);
+ return (npix);
+}
+
+
+/* subSample -- Subsample an image line. Extract the first pixel and
+ * every "step"th pixel thereafter for a total of npix pixels.
+ */
+
+static void
+subSample (a, b, npix, step)
+float *a;
+float *b;
+int npix, step;
+{
+ register int ip, i;
+
+ if (step <= 1)
+ memmove (b, a, npix);
+ else {
+ ip = 0;
+ for (i=0; i < npix; i++) {
+ b[i] = a[ip];
+ ip += step;
+ }
+ }
+}
+
+
+/* fitLine -- Fit a straight line to a data array of type real. This is
+ * an iterative fitting algorithm, wherein points further than ksigma from the
+ * current fit are excluded from the next fit. Convergence occurs when the
+ * next iteration does not decrease the number of pixels in the fit, or when
+ * there are no pixels left. The number of pixels left after pixel rejection
+ * is returned as the function value.
+ */
+
+static int
+fitLine (data, npix, zstart, zslope, krej, ngrow, maxiter)
+
+float *data; /* data to be fitted */
+int npix; /* number of pixels before rejection */
+float *zstart; /* Z-value of pixel data[1] (output) */
+float *zslope; /* dz/pixel (output) */
+float krej; /* k-sigma pixel rejection factor */
+int ngrow; /* number of pixels of growing */
+int maxiter; /* max iterations */
+{
+ int i, ngoodpix, last_ngoodpix, minpix, niter;
+ double xscale, z0, dz, o_dz, x, z, mean, sigma, threshold;
+ double sumxsqr, sumxz, sumz, sumx, rowrat;
+ float *flat, *normx;
+ char *badpix;
+
+ if (npix <= 0)
+ return (0);
+ else if (npix == 1) {
+ *zstart = data[1];
+ *zslope = 0.0;
+ return (1);
+ } else
+ xscale = 2.0 / (npix - 1);
+
+ /* Allocate a buffer for data minus fitted curve, another for the
+ * normalized X values, and another to flag rejected pixels.
+ */
+ flat = (float *) malloc (npix * sizeof (float));
+ normx = (float *) malloc (npix * sizeof (float));
+ badpix = (char *) calloc (npix, sizeof(char));
+
+ /* Compute normalized X vector. The data X values [1:npix] are
+ * normalized to the range [-1:1]. This diagonalizes the lsq matrix
+ * and reduces its condition number.
+ */
+ for (i=0; i<npix; i++)
+ normx[i] = i * xscale - 1.0;
+
+ /* Fit a line with no pixel rejection. Accumulate the elements of the
+ * matrix and data vector. The matrix M is diagonal with
+ * M[1,1] = sum x**2 and M[2,2] = ngoodpix. The data vector is
+ * DV[1] = sum (data[i] * x[i]) and DV[2] = sum (data[i]).
+ */
+ sumxsqr = 0;
+ sumxz = 0;
+ sumx = 0;
+ sumz = 0;
+
+ for (i=0; i<npix; i++) {
+ x = normx[i];
+ z = data[i];
+ sumxsqr = sumxsqr + (x * x);
+ sumxz = sumxz + z * x;
+ sumz = sumz + z;
+ }
+
+ /* Solve for the coefficients of the fitted line. */
+ z0 = sumz / npix;
+ dz = o_dz = sumxz / sumxsqr;
+
+ if (ZSC_DBG) {
+ printf ("fitLine:\n\tz0=%g dz=%g\n", z0, dz);
+ printf ("\tsumz=%g sumxz=%g sumxsqr=%g npix=%d zscale=%g\n\n",
+ sumz, sumxz, sumxsqr, npix, xscale);
+ }
+
+ /* Iterate, fitting a new line in each iteration. Compute the flattened
+ * data vector and the sigma of the flat vector. Compute the lower and
+ * upper k-sigma pixel rejection thresholds. Run down the flat array
+ * and detect pixels to be rejected from the fit. Reject pixels from
+ * the fit by subtracting their contributions from the matrix sums and
+ * marking the pixel as rejected.
+ */
+ ngoodpix = npix;
+ minpix = max (MIN_NPIXELS, (int) (npix * MAX_REJECT));
+
+ for (niter=0; niter < maxiter; niter++) {
+ last_ngoodpix = ngoodpix;
+
+ /* Subtract the fitted line from the data array. */
+ flattenData (data, flat, normx, npix, z0, dz);
+
+ /* Compute the k-sigma rejection threshold. In principle this
+ * could be more efficiently computed using the matrix sums
+ * accumulated when the line was fitted, but there are problems with
+ * numerical stability with that approach.
+ */
+ ngoodpix = computeSigma (flat, badpix, npix, &mean, &sigma);
+ threshold = sigma * krej;
+
+ /* Detect and reject pixels further than ksigma from the fitted
+ * line.
+ */
+ ngoodpix = rejectPixels (data, flat, normx,
+ badpix, npix, &sumxsqr, &sumxz, &sumx, &sumz, threshold,
+ ngrow);
+
+ /* Solve for the coefficients of the fitted line. Note that after
+ * pixel rejection the sum of the X values need no longer be zero.
+ */
+ if (ngoodpix > 0) {
+ rowrat = sumx / sumxsqr;
+ z0 = (sumz - rowrat * sumxz) / (ngoodpix - rowrat * sumx);
+ dz = (sumxz - z0 * sumx) / sumxsqr;
+ }
+
+ if (ngoodpix >= last_ngoodpix || ngoodpix < minpix)
+ break;
+ }
+
+ if (ZSC_DBG) { printf ("\tz0=%g dz=%g rowrat=%g\n", z0, dz, rowrat); }
+
+ /* Transform the line coefficients back to the X range [1:npix]. */
+ *zstart = z0 - dz;
+ *zslope = dz * xscale;
+ if (abs(*zslope) < 0.01)
+ *zslope = o_dz * xscale;
+
+ free ((float *)flat);
+ free ((float *)normx);
+ free ((char *)badpix);
+ return (ngoodpix);
+}
+
+
+/* flattenData -- Compute and subtract the fitted line from the data array,
+ * returned the flattened data in FLAT.
+ */
+
+static void
+flattenData (data, flat, x, npix, z0, dz)
+float *data; /* raw data array */
+float *flat; /* flattened data (output) */
+float *x; /* x value of each pixel */
+int npix; /* number of pixels */
+double z0, dz; /* z-intercept, dz/dx of fitted line */
+{
+ register int i;
+
+ for (i=0; i < npix; i++)
+ flat[i] = data[i] - (x[i] * dz + z0);
+}
+
+
+/* computeSigma -- Compute the root mean square deviation from the
+ * mean of a flattened array. Ignore rejected pixels.
+ */
+
+static int
+computeSigma (a, badpix, npix, mean, sigma)
+
+float *a; /* flattened data array */
+char *badpix; /* bad pixel flags (!= 0 if bad pixel) */
+int npix;
+double *mean, *sigma; /* (output) */
+{
+ float pixval;
+ int i, ngoodpix = 0;
+ double sum = 0.0, sumsq = 0.0, temp;
+
+ /* Accumulate sum and sum of squares. */
+ for (i=0; i < npix; i++) {
+ if (badpix[i] == GOOD_PIXEL) {
+ pixval = a[i];
+ ngoodpix = ngoodpix + 1;
+ sum = sum + pixval;
+ sumsq = sumsq + pixval * pixval;
+ }
+ }
+
+ /* Compute mean and sigma. */
+ switch (ngoodpix) {
+ case 0:
+ *mean = INDEF;
+ *sigma = INDEF;
+ break;
+ case 1:
+ *mean = sum;
+ *sigma = INDEF;
+ break;
+ default:
+ *mean = sum / (double) ngoodpix;
+ temp = sumsq / (double) (ngoodpix-1) -
+ (sum*sum) / (double) (ngoodpix*(ngoodpix - 1));
+ if (temp < 0) /* possible with roundoff error */
+ *sigma = 0.0;
+ else
+ *sigma = sqrt (temp);
+ }
+
+ return (ngoodpix);
+}
+
+
+/* rejectPixels -- Detect and reject pixels more than "threshold" greyscale
+ * units from the fitted line. The residuals about the fitted line are given
+ * by the "flat" array, while the raw data is in "data". Each time a pixel
+ * is rejected subtract its contributions from the matrix sums and flag the
+ * pixel as rejected. When a pixel is rejected reject its neighbors out to
+ * a specified radius as well. This speeds up convergence considerably and
+ * produces a more stringent rejection criteria which takes advantage of the
+ * fact that bad pixels tend to be clumped. The number of pixels left in the
+ * fit is returned as the function value.
+ */
+
+static int
+rejectPixels (data, flat, normx, badpix, npix,
+ sumxsqr, sumxz, sumx, sumz, threshold, ngrow)
+
+float *data; /* raw data array */
+float *flat; /* flattened data array */
+float *normx; /* normalized x values of pixels */
+char *badpix; /* bad pixel flags (!= 0 if bad pixel) */
+int npix;
+double *sumxsqr, *sumxz, /* matrix sums */
+ *sumx, *sumz;
+double threshold; /* threshold for pixel rejection */
+int ngrow; /* number of pixels of growing */
+{
+ int ngoodpix, i, j;
+ double residual, lcut, hcut;
+ double x, z;
+
+ ngoodpix = npix;
+ lcut = -threshold;
+ hcut = threshold;
+
+ for (i=0; i < npix; i++) {
+ if (badpix[i] == BAD_PIXEL)
+ ngoodpix = ngoodpix - 1;
+ else {
+ residual = flat[i];
+ if (residual < lcut || residual > hcut) {
+ /* Reject the pixel and its neighbors out to the growing
+ * radius. We must be careful how we do this to avoid
+ * directional effects. Do not turn off thresholding on
+ * pixels in the forward direction; mark them for rejection
+ * but do not reject until they have been thresholded.
+ * If this is not done growing will not be symmetric.
+ */
+ for (j=max(0,i-ngrow); j < min(npix,i+ngrow); j++) {
+ if (badpix[j] != BAD_PIXEL) {
+ if (j <= i) {
+ x = (double) normx[j];
+ z = (double) data[j];
+ *sumxsqr = *sumxsqr - (x * x);
+ *sumxz = *sumxz - z * x;
+ *sumx = *sumx - x;
+ *sumz = *sumz - z;
+ badpix[j] = BAD_PIXEL;
+ ngoodpix = ngoodpix - 1;
+ } else
+ badpix[j] = REJECT_PIXEL;
+ }
+ }
+ }
+ }
+ }
+
+ return (ngoodpix);
+}
+
+
+static int
+floatCompare (i,j)
+float *i, *j;
+{
+ /* return ((int) (*i - *j + 0.5)); */
+ return ((*i <= *j) ? -1 : 1);
+}