mirror of https://github.com/XEphem/XEphem.git
325 lines
8.2 KiB
C
325 lines
8.2 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "ip.h"
|
|
|
|
#define MAXSR 20 /* max star radius, ie, pix to noise */
|
|
#define MINSR 1 /* min star radius */
|
|
#define SKYR 2 /* measure sky @ starr * SKYR */
|
|
#define MINSS 2 /* min star separation */
|
|
#define NWALK 8 /* n pixels surrounding a pixel */
|
|
#define NCONN 3 /* min connected pixels to qualify */
|
|
#define PKP 5 /* dips must be > this % of peak-med */
|
|
|
|
static double gauss2d (Gaussian *hgp, Gaussian *vgp, int x, int y);
|
|
|
|
/* extract some "stars" from fip, no guarantees but fast.
|
|
* return malloced arrays of centroided locations sorted by brightest pixel.
|
|
* N.B. caller must always free *xpp and *ypp if return >= 0.
|
|
*/
|
|
int
|
|
quickStars (FImage *fip, ImStats *isp, int border, int burnt, double nsd,
|
|
double **xpp, double **ypp)
|
|
{
|
|
#define IFDP if (dfp && abs(x-dx)<=dr && abs(y-dy)<=dr)
|
|
CamPix *ip, *ip0 = (CamPix *)fip->image;
|
|
int bsr = border+MAXSR; /* handy border-MAXSR */
|
|
int w = fip->sw; /* handy */
|
|
int h = fip->sh; /* handy */
|
|
int walk[NWALK]; /* fast walk about */
|
|
int *br; /* temp array to sort by brightness */
|
|
int dx, dy, dr; /* dump region */
|
|
FILE *dfp; /* dump file, if any */
|
|
double *xp, *yp;
|
|
int x, y;
|
|
int nis;
|
|
|
|
/* prime the arrays */
|
|
xp = (double *) malloc (10*sizeof(double));
|
|
yp = (double *) malloc (10*sizeof(double));
|
|
br = (int *) malloc (10*sizeof(int));
|
|
if (!xp || !yp || !br) {
|
|
if (xp) free (xp);
|
|
if (yp) free (yp);
|
|
if (br) free (br);
|
|
return (-1);
|
|
}
|
|
nis = 0;
|
|
|
|
/* set up walk-about */
|
|
walk[0] = 1;
|
|
walk[1] = 1 - w;
|
|
walk[2] = - w;
|
|
walk[3] = -1 - w;
|
|
walk[4] = -1;
|
|
walk[5] = -1 + w;
|
|
walk[6] = w;
|
|
walk[7] = 1 + w;
|
|
|
|
/* check for dump file */
|
|
dfp = fopen ("x.dump", "r");
|
|
if (dfp) {
|
|
char fname[128];
|
|
int ok = fscanf (dfp, "%s %d %d %d", fname, &dx, &dy, &dr) == 4;
|
|
fclose (dfp);
|
|
dfp = ok ? fopen (fname, "w") : NULL;
|
|
}
|
|
|
|
/* scan inside border+MAXSR for local maxima */
|
|
ip = ip0 + bsr*w + bsr;
|
|
for (y = bsr; y < h-bsr; y++) {
|
|
for (x = bsr; x < w-bsr; x++) {
|
|
int p0 = (int)*ip; /* current central pixel */
|
|
double cx, cy; /* centroided position */
|
|
int sky, cut; /* sky, real star cutoff */
|
|
double std; /* 1 sigma */
|
|
int rmin, rmax; /* min and max of a given ring */
|
|
int lmax; /* rmax in last radii */
|
|
int starr; /* radius of star edge */
|
|
int mindip; /* minimum dip before peaking */
|
|
int mass; /* star brightness */
|
|
int ncon;
|
|
int i, r;
|
|
|
|
/* immediately discard burnt pixels or ones below median */
|
|
mindip = PKP*(p0 - isp->median);
|
|
if (mindip < 0) {
|
|
IFDP fprintf (dfp, "%4d %4d: %5d < median %5d\n", x, y,
|
|
p0, isp->median);
|
|
goto no;
|
|
}
|
|
if (p0 >= burnt) {
|
|
IFDP fprintf (dfp, "%4d %4d: burnt: %5d >= %5d\n", x, y,
|
|
p0, burnt);
|
|
goto no;
|
|
}
|
|
|
|
/* init ranges */
|
|
lmax = p0;
|
|
starr = 0;
|
|
|
|
/* check successively greater rings. Found star outter edge
|
|
* when max has decreased and increased again.
|
|
*/
|
|
for (r = 1; r < MAXSR; r++) {
|
|
|
|
/* get stats for ring with radius r */
|
|
ringStats (ip, w, r, NULL, &rmax, NULL);
|
|
|
|
/* reject any burnt pixels */
|
|
if (rmax >= burnt) {
|
|
IFDP fprintf (dfp, "%4d %4d: ring %d burnt %5d\n",
|
|
x, y, r, rmax);
|
|
goto no;
|
|
}
|
|
|
|
/* found star edge when max first increases.
|
|
* allow for a little benign bumpiness near the top
|
|
*/
|
|
if (rmax > lmax && 100*(p0-lmax) > mindip) {
|
|
starr = r - 1; /* stats are for last radius */
|
|
break;
|
|
}
|
|
|
|
/* p0 is not a peak if neighbors are brighter */
|
|
if (rmax > p0) {
|
|
IFDP fprintf (dfp,
|
|
"%4d %4d: %5d but %5d in ring @ r= %d\n",
|
|
x, y, p0, rmax, r);
|
|
goto no;
|
|
}
|
|
|
|
/* save last ring extreme */
|
|
lmax = rmax;
|
|
}
|
|
|
|
if (r == MAXSR) {
|
|
IFDP fprintf (dfp, "%4d %4d: starr %d too large\n",
|
|
x, y, r);
|
|
goto no;
|
|
}
|
|
if (starr < MINSR) {
|
|
IFDP fprintf (dfp, "%4d %4d: starr %d too small\n",
|
|
x, y, starr);
|
|
goto no;
|
|
}
|
|
|
|
/* estimate sky and noise and set cutoff */
|
|
r = SKYR*starr;
|
|
if (r > MAXSR)
|
|
r = MAXSR;
|
|
ringStats (ip, w, r, &rmin, NULL, &sky);
|
|
std = (sky - rmin)/3.0; /* ?? */
|
|
cut = sky + (int)(nsd*std);
|
|
if (p0 < cut) {
|
|
IFDP fprintf (dfp,
|
|
"%4d %4d: %5d too dim r= %d sky= %d std= %g\n",
|
|
x, y, p0, starr, sky, std);
|
|
goto no;
|
|
}
|
|
|
|
/* want several connected neighbors above cutoff */
|
|
ncon = 0;
|
|
for (i = 0; i < NWALK+NCONN; i++)
|
|
if (ip[walk[i%NWALK]] >= cut) {
|
|
if (++ncon == NCONN)
|
|
break;
|
|
} else
|
|
ncon = 0;
|
|
if (ncon < NCONN) {
|
|
IFDP fprintf (dfp,
|
|
"%4d %4d: only %d connected neighbors above %d\n",
|
|
x, y, ncon, cut);
|
|
goto no;
|
|
}
|
|
|
|
/* find centroided position */
|
|
starCentroid (ip, w, starr, sky, &cx, &cy, &mass);
|
|
cx += x;
|
|
cy += y;
|
|
|
|
/* must be a few pixels from any other */
|
|
for (i = 0; i < nis; i++)
|
|
if (fabs(cx-xp[i]) < MINSS && fabs(cy-yp[i]) < MINSS) {
|
|
IFDP fprintf (dfp,
|
|
"%4d %4d: %5d dup @ %4.0f %4.0f\n",
|
|
x, y, p0, xp[i], yp[i]);
|
|
goto no;
|
|
}
|
|
|
|
/* Ok! insert in order of decreasing brightness */
|
|
xp = (double *) realloc ((char *)xp, (nis+1)*sizeof(double));
|
|
yp = (double *) realloc ((char *)yp, (nis+1)*sizeof(double));
|
|
br = (int *) realloc ((char *)br, (nis+1)*sizeof(int));
|
|
if (!xp || !yp || !br) {
|
|
if (xp) free (xp);
|
|
if (yp) free (yp);
|
|
if (br) free (br);
|
|
return (-1);
|
|
}
|
|
for (i = nis; i > 0 && mass > br[i-1]; --i) {
|
|
br[i] = br[i-1];
|
|
xp[i] = xp[i-1];
|
|
yp[i] = yp[i-1];
|
|
}
|
|
br[i] = mass;
|
|
xp[i] = cx;
|
|
yp[i] = cy;
|
|
|
|
IFDP fprintf (dfp,
|
|
"%4d %4d: OK %5d > %5d %6.1f %6.1f r= %d sky= %d std= %g\n",
|
|
x, y, p0, cut, cx, cy, starr, sky, std);
|
|
|
|
/* inc number of stars found */
|
|
nis++;
|
|
|
|
no:
|
|
|
|
/* move to next pixel right */
|
|
ip++;
|
|
}
|
|
|
|
/* move to start of next row */
|
|
ip += 2*bsr;
|
|
}
|
|
|
|
free ((char *)br);
|
|
*xpp = xp;
|
|
*ypp = yp;
|
|
if (dfp)
|
|
fclose (dfp);
|
|
return (nis);
|
|
}
|
|
|
|
/* given a region that presumably contains a single star, fill in sp
|
|
* with best-fit 2d commensurate gaussian.
|
|
* return 0 if ok, else -1
|
|
* N.B. we assume rp has already been clamped.
|
|
*/
|
|
int
|
|
getStar (ImRegion *rp, Star *sp)
|
|
{
|
|
CamPix *im = &rp->im[rp->ry*rp->iw + rp->rx];
|
|
int wrap = rp->iw - rp->rw;
|
|
int nr = rp->rw * rp->rh;
|
|
double sum = 0, sum2 = 0;
|
|
double v;
|
|
int x, y;
|
|
|
|
/* on guard */
|
|
if (nr < 2) {
|
|
printf ("getStar called with region of 1\n");
|
|
return (-1);
|
|
}
|
|
|
|
/* find commensurate gaussians */
|
|
if (gauss2fit (rp, &sp->hg, &sp->vg) < 0)
|
|
return (-1);
|
|
|
|
/* position is with respect to corner of region */
|
|
sp->x = rp->rx + sp->hg.m;
|
|
sp->y = rp->ry + sp->vg.m;
|
|
|
|
/* compare ideal to real to estimate noise */
|
|
for (y = 0; y < rp->rh; y++) {
|
|
for (x = 0; x < rp->rw; x++) {
|
|
double ideal = gauss2d (&sp->hg, &sp->vg, x, y);
|
|
double real = (double)*im++;
|
|
double err = ideal - real;
|
|
sum += err;
|
|
sum2 += sqr(err);
|
|
}
|
|
im += wrap;
|
|
}
|
|
|
|
/* error is STARSIGMA sigma noise */
|
|
v = (sum2 - sum*sum/nr)/(nr-1);
|
|
sp->err = v <= 0 ? 0 : STARSIGMA*sqrt(v);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* find the magnitude of s1 wrt s0 and the error in the estimate.
|
|
* gaussian volume taken to be proportional to height * sigmax * sigmay.
|
|
* return 0 if ok, else -1 if numerical troubles.
|
|
*/
|
|
int
|
|
cmpStars (Star *s0, Star *s1, double *magp, double *errp)
|
|
{
|
|
double v0, v1, r;
|
|
|
|
/* magnitude is ratio of guassian volumes */
|
|
v0 = s0->hg.A * s0->hg.s * s0->vg.s;
|
|
v1 = s1->hg.A * s1->hg.s * s1->vg.s;
|
|
if (v0 == 0 || (r = v1/v0) <= 0)
|
|
return (-1);
|
|
*magp = -2.511886*log10(r); /* + is dimmer */
|
|
|
|
/* error = (log(largest ratio) - log(smallest ratio))/2.
|
|
* = (log((largest ratio) / (smallest ratio)))/2.
|
|
* final /2 because we want to report as +/-
|
|
*/
|
|
v1 = (s1->hg.A + s1->err)/(s0->hg.A - s0->err); /* largest */
|
|
v0 = (s1->hg.A - s1->err)/(s0->hg.A + s0->err); /* smallest */
|
|
if (v0 == 0)
|
|
return (-1);
|
|
*errp = (2.511886/2)*log10(fabs(v1/v0));
|
|
|
|
/* made it */
|
|
return (0);
|
|
}
|
|
|
|
|
|
/* compute value of 2d gaussian at [x,y] */
|
|
static double
|
|
gauss2d (Gaussian *hgp, Gaussian *vgp, int x, int y)
|
|
{
|
|
return (hgp->B + hgp->A*exp(-.5*(sqr((x-hgp->m)/hgp->s)+
|
|
sqr((y-vgp->m)/vgp->s))));
|
|
}
|
|
|
|
/* For RCS Only -- Do Not Edit */
|
|
static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: stars.c,v $ $Date: 2002/01/15 19:48:27 $ $Revision: 1.7 $ $Name: $"};
|