From 016b25b23f55c8c9c6b2814581b271b9a2239425 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:29:28 +0100 Subject: [PATCH 01/47] Modify sep.pyx - allow a segmap array to be passed to sep.extract(). --- sep.pyx | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/sep.pyx b/sep.pyx index ca1795b..1bd1482 100644 --- a/sep.pyx +++ b/sep.pyx @@ -69,6 +69,9 @@ cdef extern from "sep.h": int ndtype int mdtype int sdtype + int *segids + int *idcounts + int numids int w int h double noiseval @@ -301,6 +304,7 @@ cdef int _parse_arrays(np.ndarray data, err, var, mask, segmap, im.noise = NULL im.mask = NULL im.segmap = NULL + im.numids = 0 im.ndtype = 0 im.mdtype = 0 im.noiseval = 0.0 @@ -592,7 +596,7 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, np.ndarray filter_kernel=default_kernel, filter_type='matched', int deblend_nthresh=32, double deblend_cont=0.005, bint clean=True, double clean_param=1.0, - segmentation_map=False): + segmentation_map=None): """extract(data, thresh, err=None, mask=None, minarea=5, filter_kernel=default_kernel, filter_type='matched', deblend_nthresh=32, deblend_cont=0.005, clean=True, @@ -697,9 +701,26 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, cdef np.int32_t *segmap_ptr cdef int *objpix cdef sep_image im + cdef np.int32_t[:] idbuf, countbuf # parse arrays - _parse_arrays(data, err, var, mask, None, &im) + if type(segmentation_map) is np.ndarray: + _parse_arrays(data, err, var, mask, segmentation_map, &im) + + ids, counts = np.unique(segmentation_map, return_counts=True) + + # Remove non-object IDs: + filter_ids = ids>0 + segids = np.ascontiguousarray(ids[filter_ids].astype(dtype=np.int32)) + idcounts = np.ascontiguousarray(counts[filter_ids].astype(dtype=np.int32)) + + idbuf = segids.view(dtype=np.int32) + countbuf = idcounts.view(dtype=np.int32) + im.segids = &idbuf[0] + im.idcounts = &countbuf[0] + im.numids = len(segids) + else: + _parse_arrays(data, err, var, mask, None, &im) im.maskthresh = maskthresh if gain is not None: im.gain = gain @@ -802,7 +823,7 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, result['flag'][i] = catalog.flag[i] # construct a segmentation map, if it was requested. - if segmentation_map: + if type(segmentation_map) is np.ndarray or segmentation_map: # Note: We have to write out `(data.shape[0], data.shape[1])` because # because Cython turns `data.shape` later into an int pointer when # the function argument is typed as np.ndarray. @@ -817,7 +838,7 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, # Free the C catalog sep_catalog_free(catalog) - if segmentation_map: + if type(segmentation_map) is np.ndarray or segmentation_map: return result, segmap else: return result From db39dfb80333052c57856d72cc8e693f520fd196 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 27 Nov 2023 16:36:20 +0100 Subject: [PATCH 02/47] Modify the corresponding headers. --- src/extract.h | 6 +++--- src/sep.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/extract.h b/src/extract.h index b054604..c40bb85 100644 --- a/src/extract.h +++ b/src/extract.h @@ -42,9 +42,9 @@ typedef enum {COMPLETE, INCOMPLETE, NONOBJECT, OBJECT} pixstatus; /* Temporary object parameters during extraction */ typedef struct structinfo { - LONG pixnb; /* Number of pixels included */ - LONG firstpix; /* Pointer to first pixel of pixlist */ - LONG lastpix; /* Pointer to last pixel of pixlist */ + long pixnb; /* Number of pixels included */ + long firstpix; /* Pointer to first pixel of pixlist */ + long lastpix; /* Pointer to last pixel of pixlist */ short flag; /* Extraction flag */ } infostruct; diff --git a/src/sep.h b/src/sep.h index cb709c4..5e41a66 100644 --- a/src/sep.h +++ b/src/sep.h @@ -74,6 +74,9 @@ typedef struct { int ndtype; /* element type of noise */ int mdtype; /* element type of mask */ int sdtype; /* element type of segmap */ + int *segids; /* unique ids in segmap */ + int *idcounts; /* counts of unique ids in segmap */ + int numids; /* total number of unique ids in segmap */ int w; /* array width */ int h; /* array height */ double noiseval; /* scalar noise value; used only if noise == NULL */ From 63758634696c3dc932ebae1b970150820868e218 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:13:54 +0100 Subject: [PATCH 03/47] Modify extract.c - analyse objects in existing segmap. --- src/extract.c | 264 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 199 insertions(+), 65 deletions(-) diff --git a/src/extract.c b/src/extract.c index 40d9fb3..3087c0c 100644 --- a/src/extract.c +++ b/src/extract.c @@ -53,9 +53,10 @@ size_t sep_get_extract_pixstack() } int sortit(infostruct *info, objliststruct *objlist, int minarea, - objliststruct *finalobjlist, - int deblend_nthresh, double deblend_mincont, double gain, - deblendctx *deblendctx); + objliststruct *finalobjlist, int deblend_nthresh, + double deblend_mincont, double gain, deblendctx *deblendctx); +int segsortit(infostruct *info, objliststruct *objlist, + objliststruct *finalobjlist, double gain); void plistinit(int hasconv, int hasvar); void clean(objliststruct *objlist, double clean_param, int *survives); int convert_to_catalog(objliststruct *objlist, const int *survives, @@ -71,8 +72,7 @@ void arraybuffer_free(arraybuffer *buf); /* initialize buffer */ /* bufw must be less than or equal to w */ int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, int w, int h, - int bufw, int bufh) -{ + int bufw, int bufh) { int status, yl; status = RETURN_OK; @@ -111,8 +111,7 @@ int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, int w, int h, } /* read a line into the buffer at the top, shifting all lines down one */ -void arraybuffer_readline(arraybuffer *buf) -{ +void arraybuffer_readline(arraybuffer *buf) { PIXTYPE *line; int y; @@ -129,8 +128,7 @@ void arraybuffer_readline(arraybuffer *buf) buf->lastline); } -void arraybuffer_free(arraybuffer *buf) -{ +void arraybuffer_free(arraybuffer *buf) { free(buf->bptr); buf->bptr = NULL; } @@ -153,36 +151,35 @@ void arraybuffer_free(arraybuffer *buf) * So, this routine sets masked pixels to zero in the image buffer and * infinity in the noise buffer (if present). It affects the first */ -void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf) -{ +void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf) { int i; - for (i=0; ibw; i++) - { - if (mbuf->lastline[i] > 0.0) - { - imbuf->lastline[i] = 0.0; - if (nbuf) - nbuf->lastline[i] = BIG; - } + for (i=0; ibw; i++) { + if (mbuf->lastline[i] > 0.0) { + imbuf->lastline[i] = 0.0; + if (nbuf) { + nbuf->lastline[i] = BIG; + } } + } } /****************************** extract **************************************/ int sep_extract(const sep_image *image, float thresh, int thresh_type, int minarea, const float *conv, int convw, int convh, - int filter_type, int deblend_nthresh, double deblend_cont, - int clean_flag, double clean_param, - sep_catalog **catalog) -{ - arraybuffer dbuf, nbuf, mbuf; + int filter_type, int deblend_nthresh, double deblend_cont, + int clean_flag, double clean_param, sep_catalog **catalog) { + + arraybuffer dbuf, nbuf, mbuf, sbuf; infostruct curpixinfo, initinfo, freeinfo; objliststruct objlist; char newmarker; size_t mem_pixstack; int nposize, oldnposize; int w, h; - int co, i, luflag, pstop, xl, xl2, yl, cn; + int co, i, j, luflag, pstop, xl, xl2, yl, cn; + int ididx, numids, totnpix; + long prevpix; int stacksize, convn, status; int bufh; int isvarthresh, isvarnoise; @@ -195,7 +192,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, objliststruct *finalobjlist; pliststruct *pixel, *pixt; char *marker; - PIXTYPE *scan, *cdscan, *wscan, *dummyscan; + PIXTYPE *scan, *cdscan, *wscan, *dummyscan, *sscan; PIXTYPE *sigscan, *workscan; float *convnorm; int *start, *end, *survives; @@ -207,7 +204,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, status = RETURN_OK; pixel = NULL; convnorm = NULL; - scan = wscan = cdscan = dummyscan = NULL; + scan = wscan = cdscan = dummyscan = sscan = NULL; sigscan = workscan = NULL; info = NULL; store = NULL; @@ -221,6 +218,8 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, sum = 0.0; w = image->w; h = image->h; + numids = (image->numids) ? image->numids : 1; + infostruct idinfo[numids]; isvarthresh = 0; relthresh = 0.0; pixvar = 0.0; @@ -230,12 +229,23 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, mem_pixstack = sep_get_extract_pixstack(); + if (image->segmap) { + totnpix = 0; + for (i=0; iidcounts[i]; + } + if (totnpix>mem_pixstack) { + goto exit; + } + mem_pixstack = totnpix + 1; + } + /* seed the random number generator consistently on each call to get * consistent results. rand_r() is used in deblending. */ randseed = 1; /* Noise characteristics of the image: None, scalar or variable? */ - if (image->noise_type == SEP_NOISE_NONE) { } /* nothing to do */ + if (image->noise_type == SEP_NOISE_NONE) {} /* nothing to do */ else if (image->noise == NULL) { /* noise is constant; we can set pixel noise now. */ if (image->noise_type == SEP_NOISE_STDDEV) { @@ -308,6 +318,12 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, stacksize, bufh); if (status != RETURN_OK) goto exit; } + if (image->segmap) { + status = arraybuffer_init(&sbuf, image->segmap, image->sdtype, w, h, + stacksize, bufh); + if (status != RETURN_OK) + goto exit; + } /* `scan` (or `wscan`) is always a pointer to the current line being * processed. It might be the only line in the buffer, or it might be the @@ -321,14 +337,27 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, initinfo.flag = 0; initinfo.firstpix = initinfo.lastpix = -1; - for (xl=0; xlsegmap) { + sscan = sbuf.midline; + for (i=0; isegmap) { + objlist.nobj = numids; + QMALLOC(objlist.obj, objstruct, numids, status); + } else { + objlist.nobj = 1; + } curpixinfo.pixnb = 1; /* Init finalobjlist */ @@ -340,11 +369,10 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, /* Allocate memory for the pixel list */ plistinit((conv != NULL), (image->noise_type != SEP_NOISE_NONE)); - if (!(pixel = objlist.plist = malloc(nposize=mem_pixstack*plistsize))) - { - status = MEMORY_ALLOC_ERROR; - goto exit; - } + if (!(pixel = objlist.plist = malloc(nposize=mem_pixstack*plistsize))) { + status = MEMORY_ALLOC_ERROR; + goto exit; + } /*----- at the beginning, "free" object fills the whole pixel list */ freeinfo.firstpix = 0; @@ -359,37 +387,34 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (!(conv && isvarnoise)) filter_type = SEP_FILTER_CONV; - if (conv) - { - /* allocate memory for convolved buffers */ - QMALLOC(cdscan, PIXTYPE, stacksize, status); - if (filter_type == SEP_FILTER_MATCHED) - { - QMALLOC(sigscan, PIXTYPE, stacksize, status); - QMALLOC(workscan, PIXTYPE, stacksize, status); - } - - /* normalize the filter */ - convn = convw * convh; - QMALLOC(convnorm, PIXTYPE, convn, status); - for (i=0; isegmap) { + arraybuffer_readline(&sbuf); + } /* filter the lines */ if (conv) @@ -474,6 +502,77 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, } /* luflag: is pixel above thresh (Y/N)? */ + /* First check if segmap exists */ + if (image->segmap) { + if (sscan[xl]>0) { + if (xl == 0 || xl == w - 1) + curpixinfo.flag |= SEP_OBJ_TRUNC; + + prevpix = 0; + for (ididx=0; ididxsegids[ididx]==(int)sscan[xl]) { + // printf("\nx=%d, y=%d", xl, yl); + // printf("\nCurrent prevpix=%ld, ", prevpix); + prevpix += idinfo[ididx].pixnb; + // printf("new prevpix: %ld, ", prevpix); + pixt = pixel + prevpix*plistsize; + // printf("found pixt, "); + + PLIST(pixt, x) = xl; + PLIST(pixt, y) = yl; + PLIST(pixt, value) = scan[xl]; + if (PLISTEXIST(cdvalue)) + PLISTPIX(pixt, cdvalue) = cdnewsymbol; + if (PLISTEXIST(var)) + PLISTPIX(pixt, var) = pixvar; + if (PLISTEXIST(thresh)) + PLISTPIX(pixt, thresh) = thresh; + // printf("\nnextpix=%d", PLIST(pixt, nextpix)); + + // printf("Finished plist edits"); + + if (idinfo[ididx].pixnb == 0) { + // printf("\nFirst pixel, ididx=%d, ", ididx); + fflush(stdout); + idinfo[ididx].firstpix = prevpix*plistsize; + idinfo[ididx].pixnb = 1; + // printf("pixnb=%ld, ", idinfo[ididx].pixnb); + fflush(stdout); + } else if (idinfo[ididx].pixnb == image->idcounts[ididx]-1) { + // printf("\nLast pixel, ididx=%d, ", ididx); + fflush(stdout); + idinfo[ididx].pixnb++; + idinfo[ididx].lastpix = prevpix*plistsize; + PLIST(pixt, nextpix) = -1; + // printf("pixnb=%ld, ", idinfo[ididx].pixnb); + fflush(stdout); + } else { + // printf("\nAdding pixel, ididx=%d, ", ididx); + fflush(stdout); + // idinfo[ididx].pixptr[idinfo[ididx].pixnb] = prevpix; + idinfo[ididx].pixnb++; + // printf("pixnb=%ld, ", idinfo[ididx].pixnb); + fflush(stdout); + }; + break; + } else { + prevpix += image->idcounts[ididx]; + } + } + + // pixt = pixel + (cn = freeinfo.firstpix); + // freeinfo.firstpix = PLIST(pixt, nextpix); + // // // printf("fp: %d, ", freeinfo.firstpix); + // curpixinfo.lastpix = curpixinfo.firstpix = cn; + + + + + + } + } else { + + if (filter_type == SEP_FILTER_MATCHED) luflag = ((xl != w) && (sigscan[xl] > relthresh))? 1: 0; else @@ -674,11 +773,16 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, co--; } } + } } /*------------ End of the loop over the x's -----------------------*/ } /*---------------- End of the loop over the y's -----------------------*/ - + if (image->segmap) { + for (i = 0; i < numids; i++) { + status = segsortit(&idinfo[i], &objlist, finalobjlist, image->gain); + } + } else { /* convert `finalobjlist` to an array of `sepobj` structs */ /* if cleaning, see which objects "survive" cleaning. */ if (clean_flag) @@ -694,7 +798,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, QMALLOC(survives, int, finalobjlist->nobj, status); clean(finalobjlist, clean_param, survives); } - + } /* convert to output catalog */ QCALLOC(cat, sep_catalog, 1, status); status = convert_to_catalog(finalobjlist, survives, cat, w, 1); @@ -744,6 +848,36 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, return status; } +int segsortit(infostruct *info, objliststruct *objlist, + objliststruct *finalobjlist, double gain) { + objliststruct objlistout, *objlist2; + objstruct obj; + int i, status; + + status = RETURN_OK; + + /*----- Allocate memory to store object data */ + objlist->obj = &obj; + objlist->nobj = 1; + + memset(&obj, 0, (size_t)sizeof(objstruct)); + objlist->npix = info->pixnb; + obj.firstpix = info->firstpix; + obj.lastpix = info->lastpix; + obj.flag = info->flag; + + obj.thresh = PLISTPIX(objlist->plist+info->lastpix, thresh); + + analyse(0, objlist, 1, gain); + + status = addobjdeep(0, objlist, finalobjlist); + + if (status != RETURN_OK) + goto exit; + +exit: + return status; +} /********************************* sortit ************************************/ /* From 9fc8c81c54edc782f89ab014695b4f604f06f711 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 27 Nov 2023 17:56:02 +0100 Subject: [PATCH 04/47] Detailed changes, some formatting fixes. --- CHANGES.md | 14 ++ sep.pyx | 2 +- src/extract.c | 636 +++++++++++++++++++++++--------------------------- 3 files changed, 301 insertions(+), 351 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5bf0dc1..46ad406 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,17 @@ +v1.3.0dev (27 November 2023) +==================== + +* The `segmentation_map` argument of `sep.extract()` will now accept either an + array or boolean. If an existing segmentation map is passed, the object + detection stage is skipped, and sources will be individually analysed + according to the provided map. This change is backwards-compatible with + respect to the Python module. + + Please note that as no deblending is performed, + the calculated thresholds (and any dependent parameters) may not be the same + as originally derived. + + v1.2.1 (1 June 2022) ==================== diff --git a/sep.pyx b/sep.pyx index 1bd1482..f2c5d3e 100644 --- a/sep.pyx +++ b/sep.pyx @@ -15,7 +15,7 @@ from cpython.version cimport PY_MAJOR_VERSION np.import_array() # To access the numpy C-API. -__version__ = "1.2.1" +__version__ = "1.3.0dev" # ----------------------------------------------------------------------------- # Definitions from the SEP C library diff --git a/src/extract.c b/src/extract.c index 3087c0c..09b4408 100644 --- a/src/extract.c +++ b/src/extract.c @@ -309,15 +309,15 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, bufh); if (status != RETURN_OK) goto exit; if (isvarnoise) { - status = arraybuffer_init(&nbuf, image->noise, image->ndtype, w, h, - stacksize, bufh); - if (status != RETURN_OK) goto exit; - } + status = arraybuffer_init(&nbuf, image->noise, image->ndtype, w, h, + stacksize, bufh); + if (status != RETURN_OK) goto exit; + } if (image->mask) { - status = arraybuffer_init(&mbuf, image->mask, image->mdtype, w, h, - stacksize, bufh); - if (status != RETURN_OK) goto exit; - } + status = arraybuffer_init(&mbuf, image->mask, image->mdtype, w, h, + stacksize, bufh); + if (status != RETURN_OK) goto exit; + } if (image->segmap) { status = arraybuffer_init(&sbuf, image->segmap, image->sdtype, w, h, stacksize, bufh); @@ -391,9 +391,9 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, /* allocate memory for convolved buffers */ QMALLOC(cdscan, PIXTYPE, stacksize, status); if (filter_type == SEP_FILTER_MATCHED) { - QMALLOC(sigscan, PIXTYPE, stacksize, status); - QMALLOC(workscan, PIXTYPE, stacksize, status); - } + QMALLOC(sigscan, PIXTYPE, stacksize, status); + QMALLOC(workscan, PIXTYPE, stacksize, status); + } /* normalize the filter */ convn = convw * convh; @@ -412,97 +412,87 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, ps = COMPLETE; cs = NONOBJECT; - /* Need an empty line for Lutz' algorithm to end gracely */ - if (yl==h) { - if (conv) { - free(cdscan); // cdscan set to dummyscan below - if (filter_type == SEP_FILTER_MATCHED) - { - for (xl=0; xlmask) - { - arraybuffer_readline(&mbuf); - apply_mask_line(&mbuf, &dbuf, (isvarnoise? &nbuf: NULL)); - } - if (image->segmap) { - arraybuffer_readline(&sbuf); + /* Need an empty line for Lutz' algorithm to end gracely */ + if (yl==h) { + if (conv) { + free(cdscan); // cdscan set to dummyscan below + if (filter_type == SEP_FILTER_MATCHED) { + for (xl=0; xlmask) { + arraybuffer_readline(&mbuf); + apply_mask_line(&mbuf, &dbuf, (isvarnoise? &nbuf: NULL)); + } + if (image->segmap) { + arraybuffer_readline(&sbuf); + } - /* filter the lines */ - if (conv) - { - status = convolve(&dbuf, yl, convnorm, convw, convh, cdscan); - if (status != RETURN_OK) - goto exit; + /* filter the lines */ + if (conv) { + status = convolve(&dbuf, yl, convnorm, convw, convh, cdscan); + if (status != RETURN_OK) + goto exit; - if (filter_type == SEP_FILTER_MATCHED) - { - status = matched_filter(&dbuf, &nbuf, yl, convnorm, convw, - convh, workscan, sigscan, - image->noise_type); + if (filter_type == SEP_FILTER_MATCHED) { + status = matched_filter(&dbuf, &nbuf, yl, convnorm, convw, + convh, workscan, sigscan, + image->noise_type); - if (status != RETURN_OK) - goto exit; - } - } - else - { + if (status != RETURN_OK) + goto exit; + } + } else { cdscan = scan; } - } + } - trunflag = (yl==0 || yl==h-1)? SEP_OBJ_TRUNC: 0; + trunflag = (yl==0 || yl==h-1)? SEP_OBJ_TRUNC: 0; - for (xl=0; xl<=w; xl++) - { - if (xl == w) - cdnewsymbol = -BIG; - else - cdnewsymbol = cdscan[xl]; + for (xl=0; xl<=w; xl++) { - newmarker = marker[xl]; /* marker at this pixel */ - marker[xl] = 0; + if (xl == w) + cdnewsymbol = -BIG; + else + cdnewsymbol = cdscan[xl]; - curpixinfo.flag = trunflag; + newmarker = marker[xl]; /* marker at this pixel */ + marker[xl] = 0; - /* set pixel variance/noise based on noise array */ - if (isvarthresh) { - if (xl == w || yl == h) { - pixsig = pixvar = 0.0; - } - else if (image->noise_type == SEP_NOISE_VAR) { - pixvar = wscan[xl]; - pixsig = sqrt(pixvar); - } - else if (image->noise_type == SEP_NOISE_STDDEV) { - pixsig = wscan[xl]; - pixvar = pixsig * pixsig; - } - else { - status = UNKNOWN_NOISE_TYPE; - goto exit; - } + curpixinfo.flag = trunflag; - /* set `thresh` (This is needed later, even - * if filter_type is SEP_FILTER_MATCHED */ - thresh = relthresh * pixsig; - } + /* set pixel variance/noise based on noise array */ + if (isvarthresh) { + if (xl == w || yl == h) { + pixsig = pixvar = 0.0; + } + else if (image->noise_type == SEP_NOISE_VAR) { + pixvar = wscan[xl]; + pixsig = sqrt(pixvar); + } + else if (image->noise_type == SEP_NOISE_STDDEV) { + pixsig = wscan[xl]; + pixvar = pixsig * pixsig; + } + else { + status = UNKNOWN_NOISE_TYPE; + goto exit; + } - /* luflag: is pixel above thresh (Y/N)? */ - /* First check if segmap exists */ + /* set `thresh` (This is needed later, even + * if filter_type is SEP_FILTER_MATCHED */ + thresh = relthresh * pixsig; + } + + /* luflag: is pixel above thresh (Y/N)? */ + /* First check if segmap exists */ if (image->segmap) { if (sscan[xl]>0) { if (xl == 0 || xl == w - 1) @@ -511,289 +501,237 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, prevpix = 0; for (ididx=0; ididxsegids[ididx]==(int)sscan[xl]) { - // printf("\nx=%d, y=%d", xl, yl); - // printf("\nCurrent prevpix=%ld, ", prevpix); + prevpix += idinfo[ididx].pixnb; - // printf("new prevpix: %ld, ", prevpix); pixt = pixel + prevpix*plistsize; - // printf("found pixt, "); PLIST(pixt, x) = xl; PLIST(pixt, y) = yl; PLIST(pixt, value) = scan[xl]; - if (PLISTEXIST(cdvalue)) + if (PLISTEXIST(cdvalue)) { PLISTPIX(pixt, cdvalue) = cdnewsymbol; - if (PLISTEXIST(var)) + }; + if (PLISTEXIST(var)) { PLISTPIX(pixt, var) = pixvar; - if (PLISTEXIST(thresh)) + }; + if (PLISTEXIST(thresh)) { PLISTPIX(pixt, thresh) = thresh; - // printf("\nnextpix=%d", PLIST(pixt, nextpix)); - - // printf("Finished plist edits"); + }; if (idinfo[ididx].pixnb == 0) { - // printf("\nFirst pixel, ididx=%d, ", ididx); - fflush(stdout); idinfo[ididx].firstpix = prevpix*plistsize; idinfo[ididx].pixnb = 1; - // printf("pixnb=%ld, ", idinfo[ididx].pixnb); - fflush(stdout); - } else if (idinfo[ididx].pixnb == image->idcounts[ididx]-1) { - // printf("\nLast pixel, ididx=%d, ", ididx); - fflush(stdout); + } + else if (idinfo[ididx].pixnb == image->idcounts[ididx]-1) { idinfo[ididx].pixnb++; idinfo[ididx].lastpix = prevpix*plistsize; PLIST(pixt, nextpix) = -1; - // printf("pixnb=%ld, ", idinfo[ididx].pixnb); - fflush(stdout); } else { - // printf("\nAdding pixel, ididx=%d, ", ididx); - fflush(stdout); - // idinfo[ididx].pixptr[idinfo[ididx].pixnb] = prevpix; idinfo[ididx].pixnb++; - // printf("pixnb=%ld, ", idinfo[ididx].pixnb); - fflush(stdout); }; break; } else { prevpix += image->idcounts[ididx]; } } - - // pixt = pixel + (cn = freeinfo.firstpix); - // freeinfo.firstpix = PLIST(pixt, nextpix); - // // // printf("fp: %d, ", freeinfo.firstpix); - // curpixinfo.lastpix = curpixinfo.firstpix = cn; - - - - - } } else { - - if (filter_type == SEP_FILTER_MATCHED) - luflag = ((xl != w) && (sigscan[xl] > relthresh))? 1: 0; - else - luflag = cdnewsymbol > thresh? 1: 0; - - if (luflag) - { - /* flag the current object if we're near the image bounds */ - if (xl==0 || xl==w-1) - curpixinfo.flag |= SEP_OBJ_TRUNC; - - /* point pixt to first free pixel in pixel list */ - /* and increment the "first free pixel" */ - pixt = pixel + (cn=freeinfo.firstpix); - freeinfo.firstpix = PLIST(pixt, nextpix); - curpixinfo.lastpix = curpixinfo.firstpix = cn; - - /* set values for the new pixel */ - PLIST(pixt, nextpix) = -1; - PLIST(pixt, x) = xl; - PLIST(pixt, y) = yl; - PLIST(pixt, value) = scan[xl]; - if (PLISTEXIST(cdvalue)) - PLISTPIX(pixt, cdvalue) = cdnewsymbol; - if (PLISTEXIST(var)) - PLISTPIX(pixt, var) = pixvar; - if (PLISTEXIST(thresh)) - PLISTPIX(pixt, thresh) = thresh; - - /* Check if we have run out of free pixels in objlist.plist */ - if (freeinfo.firstpix==freeinfo.lastpix) - { - status = PIXSTACK_FULL; - sprintf(errtext, - "The limit of %d active object pixels over the " - "detection threshold was reached. Check that " - "the image is background subtracted and the " - "detection threshold is not too low. If you " - "need to increase the limit, use " - "set_extract_pixstack.", - (int)mem_pixstack); - put_errdetail(errtext); - goto exit; - - /* The code in the rest of this block increases the - * stack size as needed. Currently, it is never - * executed. This is because it isn't clear that - * this is a good idea: most times when the stack - * overflows it is due to user error: too-low - * threshold or image not background subtracted. */ - - /* increase the stack size */ - oldnposize = nposize; - mem_pixstack = (int)(mem_pixstack * 2); - nposize = mem_pixstack * plistsize; - pixel = realloc(pixel, nposize); - objlist.plist = pixel; - if (!pixel) - { - status = MEMORY_ALLOC_ERROR; - goto exit; - } - - /* set next free pixel to the start of the new block - * and link up all the pixels in the new block */ - PLIST(pixel+freeinfo.firstpix, nextpix) = oldnposize; - pixt = pixel + oldnposize; - for (i=oldnposize + plistsize; i= minarea) - { - /* update threshold before object is processed */ - objlist.thresh = thresh; - - status = sortit(&info[co], &objlist, minarea, - finalobjlist, - deblend_nthresh,deblend_cont, - image->gain, &deblendctx); - if (status != RETURN_OK) - goto exit; - } - - /* free the chain-list */ - PLIST(pixel+info[co].lastpix, nextpix) = - freeinfo.firstpix; - freeinfo.firstpix = info[co].firstpix; - } - else - { - marker[end[co]] = 'F'; - store[start[co]] = info[co]; - } - co--; - ps = psstack[--pstop]; - } - } - } - /* end of if (newmarker) ------------------------------------------*/ + if (filter_type == SEP_FILTER_MATCHED) + luflag = ((xl != w) && (sigscan[xl] > relthresh))? 1: 0; + else + luflag = cdnewsymbol > thresh? 1: 0; + + if (luflag) { + /* flag the current object if we're near the image bounds */ + if (xl==0 || xl==w-1) { + curpixinfo.flag |= SEP_OBJ_TRUNC; + }; + + /* point pixt to first free pixel in pixel list */ + /* and increment the "first free pixel" */ + pixt = pixel + (cn=freeinfo.firstpix); + freeinfo.firstpix = PLIST(pixt, nextpix); + curpixinfo.lastpix = curpixinfo.firstpix = cn; + + /* set values for the new pixel */ + PLIST(pixt, nextpix) = -1; + PLIST(pixt, x) = xl; + PLIST(pixt, y) = yl; + PLIST(pixt, value) = scan[xl]; + if (PLISTEXIST(cdvalue)) { + PLISTPIX(pixt, cdvalue) = cdnewsymbol; + }; + if (PLISTEXIST(var)) { + PLISTPIX(pixt, var) = pixvar; + }; + if (PLISTEXIST(thresh)) { + PLISTPIX(pixt, thresh) = thresh; + }; + + /* Check if we have run out of free pixels in objlist.plist */ + if (freeinfo.firstpix==freeinfo.lastpix) { + status = PIXSTACK_FULL; + sprintf(errtext, + "The limit of %d active object pixels over the " + "detection threshold was reached. Check that " + "the image is background subtracted and the " + "detection threshold is not too low. If you " + "need to increase the limit, use " + "set_extract_pixstack.", + (int)mem_pixstack); + put_errdetail(errtext); + goto exit; + + /* The code in the rest of this block increases the + * stack size as needed. Currently, it is never + * executed. This is because it isn't clear that + * this is a good idea: most times when the stack + * overflows it is due to user error: too-low + * threshold or image not background subtracted. */ + + /* increase the stack size */ + oldnposize = nposize; + mem_pixstack = (int)(mem_pixstack * 2); + nposize = mem_pixstack * plistsize; + pixel = realloc(pixel, nposize); + objlist.plist = pixel; + if (!pixel) { + status = MEMORY_ALLOC_ERROR; + goto exit; + } - /* update the info or end segment */ - if (luflag) - { - update(&info[co], &curpixinfo, pixel); - } - else if (cs == OBJECT) - { - cs = NONOBJECT; - if (ps != COMPLETE) - { - marker[xl] = 'f'; - end[co] = xl; - } - else - { - ps = psstack[--pstop]; - marker[xl] = 'F'; - store[start[co]] = info[co]; - co--; - } - } + /* set next free pixel to the start of the new block + * and link up all the pixels in the new block */ + PLIST(pixel+freeinfo.firstpix, nextpix) = oldnposize; + pixt = pixel + oldnposize; + for (i=oldnposize + plistsize; i= minarea) { + /* update threshold before object is processed */ + objlist.thresh = thresh; + + status = sortit(&info[co], &objlist, minarea, + finalobjlist, deblend_nthresh, + deblend_cont, + image->gain, &deblendctx); + + if (status != RETURN_OK) { + goto exit; + }; + } + + /* free the chain-list */ + PLIST(pixel+info[co].lastpix, nextpix) = freeinfo.firstpix; + freeinfo.firstpix = info[co].firstpix; + } else { + marker[end[co]] = 'F'; + store[start[co]] = info[co]; + } + co--; + ps = psstack[--pstop]; + } + } + } + /* end of if (newmarker) ------------------------------------------*/ + + /* update the info or end segment */ + if (luflag) { + update(&info[co], &curpixinfo, pixel); + } else if (cs == OBJECT) { + cs = NONOBJECT; + if (ps != COMPLETE) { + marker[xl] = 'f'; + end[co] = xl; + } else { + ps = psstack[--pstop]; + marker[xl] = 'F'; + store[start[co]] = info[co]; + co--; + } + } } + } /*------------ End of the loop over the x's -----------------------*/ + } /*---------------- End of the loop over the y's -----------------------*/ - } /*------------ End of the loop over the x's -----------------------*/ - - } /*---------------- End of the loop over the y's -----------------------*/ if (image->segmap) { for (i = 0; i < numids; i++) { status = segsortit(&idinfo[i], &objlist, finalobjlist, image->gain); } } else { - /* convert `finalobjlist` to an array of `sepobj` structs */ - /* if cleaning, see which objects "survive" cleaning. */ - if (clean_flag) - { + /* convert `finalobjlist` to an array of `sepobj` structs */ + /* if cleaning, see which objects "survive" cleaning. */ + if (clean_flag) { /* Calculate mthresh for all objects in the list (needed for cleaning) */ - for (i=0; inobj; i++) - { - status = analysemthresh(i, finalobjlist, minarea, thresh); - if (status != RETURN_OK) - goto exit; - } + for (i=0; inobj; i++) { + status = analysemthresh(i, finalobjlist, minarea, thresh); + if (status != RETURN_OK) + goto exit; + } QMALLOC(survives, int, finalobjlist->nobj, status); clean(finalobjlist, clean_param, survives); @@ -827,22 +765,20 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, arraybuffer_free(&mbuf); if (conv) free(convnorm); - if (filter_type == SEP_FILTER_MATCHED) - { - free(sigscan); - free(workscan); - } + if (filter_type == SEP_FILTER_MATCHED) { + free(sigscan); + free(workscan); + } - if (status != RETURN_OK) - { - /* free cdscan if we didn't do it on the last `yl` line */ - if (conv && (cdscan != dummyscan)) - free(cdscan); + if (status != RETURN_OK) { + /* free cdscan if we didn't do it on the last `yl` line */ + if (conv && (cdscan != dummyscan)) + free(cdscan); - /* clean up catalog if it was allocated */ - sep_catalog_free(cat); - cat = NULL; - } + /* clean up catalog if it was allocated */ + sep_catalog_free(cat); + cat = NULL; + } *catalog = cat; return status; From cda30111ff20ea82220e799c741bc2fab0b89441 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:52:40 +0100 Subject: [PATCH 05/47] Swap to 64-bit integers. --- CHANGES.md | 5 +- sep.pyx | 123 ++++++++++++++++++++++++----------------------- src/analyse.c | 8 +-- src/aperture.c | 48 +++++++++--------- src/aperture.i | 5 +- src/background.c | 107 ++++++++++++++++++++++------------------- src/convolve.c | 12 ++--- src/deblend.c | 35 +++++++------- src/extract.c | 113 +++++++++++++++++++++++-------------------- src/extract.h | 66 ++++++++++++------------- src/lutz.c | 29 +++++------ src/sep.h | 62 ++++++++++++------------ src/sepcore.h | 14 +++--- src/util.c | 58 +++++++++++----------- 14 files changed, 354 insertions(+), 331 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 46ad406..30f531b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -v1.3.0dev (27 November 2023) +v1.3.0dev1 (27 November 2023) ==================== * The `segmentation_map` argument of `sep.extract()` will now accept either an @@ -11,6 +11,9 @@ v1.3.0dev (27 November 2023) the calculated thresholds (and any dependent parameters) may not be the same as originally derived. +* Use 64-bit integers throughout, to fix memory addressing with large arrays + (#122). + v1.2.1 (1 June 2022) ==================== diff --git a/sep.pyx b/sep.pyx index f2c5d3e..4c2335f 100644 --- a/sep.pyx +++ b/sep.pyx @@ -15,7 +15,7 @@ from cpython.version cimport PY_MAJOR_VERSION np.import_array() # To access the numpy C-API. -__version__ = "1.3.0dev" +__version__ = "1.3.0dev1" # ----------------------------------------------------------------------------- # Definitions from the SEP C library @@ -69,31 +69,31 @@ cdef extern from "sep.h": int ndtype int mdtype int sdtype - int *segids - int *idcounts - int numids - int w - int h + np.int64_t *segids + np.int64_t *idcounts + np.int64_t numids + np.int64_t w + np.int64_t h double noiseval short noise_type double gain double maskthresh ctypedef struct sep_bkg: - int w - int h + np.int64_t w + np.int64_t h float globalback float globalrms ctypedef struct sep_catalog: - int nobj - float *thresh - int *npix - int *tnpix - int *xmin - int *xmax - int *ymin - int *ymax + np.int64_t nobj + float *thresh + np.int64_t *npix + np.int64_t *tnpix + np.int64_t *xmin + np.int64_t *xmax + np.int64_t *ymin + np.int64_t *ymax double *x double *y double *x2 @@ -112,17 +112,17 @@ cdef extern from "sep.h": float *flux float *cpeak float *peak - int *xcpeak - int *ycpeak - int *xpeak - int *ypeak - short *flag - int **pix - int *objectspix + np.int64_t *xcpeak + np.int64_t *ycpeak + np.int64_t *xpeak + np.int64_t *ypeak + short *flag + np.int64_t **pix + np.int64_t *objectspix int sep_background(const sep_image *im, - int bw, int bh, - int fw, int fh, + np.int64_t bw, np.int64_t bh, + np.int64_t fw, np.int64_t fh, double fthresh, sep_bkg **bkg) @@ -138,7 +138,7 @@ cdef extern from "sep.h": int thresh_type, int minarea, float *conv, - int convw, int convh, + np.int64_t convw, np.int64_t convh, int filter_type, int deblend_nthresh, double deblend_cont, @@ -193,7 +193,7 @@ cdef extern from "sep.h": void sep_ellipse_coeffs(double a, double b, double theta, double *cxx, double *cyy, double *cxy) - void sep_set_ellipse(unsigned char *arr, int w, int h, + void sep_set_ellipse(unsigned char *arr, np.int64_t w, np.int64_t h, double x, double y, double cxx, double cyy, double cxy, double r, unsigned char val) @@ -230,7 +230,7 @@ cdef int _get_sep_dtype(dtype) except -1: raise ValueError('input array dtype not supported: {0}'.format(dtype)) -cdef int _check_array_get_dims(np.ndarray arr, int *w, int *h) except -1: +cdef int _check_array_get_dims(np.ndarray arr, np.int64_t *w, np.int64_t *h) except -1: """Check some things about an array and return dimensions""" # Raise an informative message if array is not C-contiguous @@ -297,7 +297,7 @@ cdef int _parse_arrays(np.ndarray data, err, var, mask, segmap, """Helper function for functions accepting data, error, mask & segmap arrays. Fills in an sep_image struct.""" - cdef int ew, eh, mw, mh, sw, sh + cdef np.int64_t ew, eh, mw, mh, sw, sh cdef np.uint8_t[:,:] buf, ebuf, mbuf, sbuf # Clear im fields we might not touch (everything besides data, dtype, w, h) @@ -521,7 +521,8 @@ cdef class Background: that of the original image used to measure the background. """ - cdef int w, h, status, sep_dtype + cdef np.int64_t w, h + cdef int status, sep_dtype cdef np.uint8_t[:, :] buf assert self.ptr is not NULL @@ -556,12 +557,12 @@ cdef class Background: # This needs to match the result from extract cdef packed struct Object: np.float64_t thresh - np.int_t npix - np.int_t tnpix - np.int_t xmin - np.int_t xmax - np.int_t ymin - np.int_t ymax + np.int64_t npix + np.int64_t tnpix + np.int64_t xmin + np.int64_t xmax + np.int64_t ymin + np.int64_t ymax np.float64_t x np.float64_t y np.float64_t x2 @@ -580,16 +581,18 @@ cdef packed struct Object: np.float64_t errx2 np.float64_t erry2 np.float64_t errxy - np.int_t xcpeak - np.int_t ycpeak - np.int_t xpeak - np.int_t ypeak + np.int64_t xcpeak + np.int64_t ycpeak + np.int64_t xpeak + np.int64_t ypeak np.int_t flag default_kernel = np.array([[1.0, 2.0, 1.0], [2.0, 4.0, 2.0], [1.0, 2.0, 1.0]], dtype=np.float32) +@cython.boundscheck(False) +@cython.wraparound(False) def extract(np.ndarray data not None, float thresh, err=None, var=None, gain=None, np.ndarray mask=None, double maskthresh=0.0, int minarea=5, @@ -699,9 +702,9 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, cdef float *kernelptr cdef np.int32_t[:, :] segmap_buf cdef np.int32_t *segmap_ptr - cdef int *objpix + cdef np.int64_t *objpix cdef sep_image im - cdef np.int32_t[:] idbuf, countbuf + cdef np.int64_t[:] idbuf, countbuf # parse arrays if type(segmentation_map) is np.ndarray: @@ -711,13 +714,13 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, # Remove non-object IDs: filter_ids = ids>0 - segids = np.ascontiguousarray(ids[filter_ids].astype(dtype=np.int32)) - idcounts = np.ascontiguousarray(counts[filter_ids].astype(dtype=np.int32)) + segids = np.ascontiguousarray(ids[filter_ids].astype(dtype=np.int64)) + idcounts = np.ascontiguousarray(counts[filter_ids].astype(dtype=np.int64)) - idbuf = segids.view(dtype=np.int32) - countbuf = idcounts.view(dtype=np.int32) - im.segids = &idbuf[0] - im.idcounts = &countbuf[0] + idbuf = segids.view(dtype=np.int64) + countbuf = idcounts.view(dtype=np.int64) + im.segids = &idbuf[0] + im.idcounts = &countbuf[0] im.numids = len(segids) else: _parse_arrays(data, err, var, mask, None, &im) @@ -760,12 +763,12 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, # Allocate result record array and fill it result = np.empty(catalog.nobj, dtype=np.dtype([('thresh', np.float64), - ('npix', np.int_), - ('tnpix', np.int_), - ('xmin', np.int_), - ('xmax', np.int_), - ('ymin', np.int_), - ('ymax', np.int_), + ('npix', np.int64), + ('tnpix', np.int64), + ('xmin', np.int64), + ('xmax', np.int64), + ('ymin', np.int64), + ('ymax', np.int64), ('x', np.float64), ('y', np.float64), ('x2', np.float64), @@ -784,11 +787,11 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, ('flux', np.float64), ('cpeak', np.float64), ('peak', np.float64), - ('xcpeak', np.int_), - ('ycpeak', np.int_), - ('xpeak', np.int_), - ('ypeak', np.int_), - ('flag', np.int_)])) + ('xcpeak', np.int64), + ('ycpeak', np.int64), + ('xpeak', np.int64), + ('ypeak', np.int64), + ('flag', np.int64)])) for i in range(catalog.nobj): result['thresh'][i] = catalog.thresh[i] @@ -1726,7 +1729,7 @@ def mask_ellipse(np.ndarray arr not None, x, y, a=None, b=None, theta=None, Scale factor of ellipse(s). Default is 1. """ - cdef int w, h + cdef np.int64_t w, h cdef np.uint8_t[:,:] buf cdef double cxx_, cyy_, cxy_ diff --git a/src/analyse.c b/src/analyse.c index 9dca687..fdf2258 100644 --- a/src/analyse.c +++ b/src/analyse.c @@ -107,8 +107,8 @@ void preanalyse(int no, objliststruct *objlist) pliststruct *pixel = objlist->plist, *pixt; PIXTYPE peak, cpeak, val, cval; double rv; - int x, y, xmin,xmax, ymin,ymax, fdnpix; - int xpeak, ypeak, xcpeak, ycpeak; + int64_t x, y, xmin,xmax, ymin,ymax, fdnpix; + int64_t xpeak, ypeak, xcpeak, ycpeak; /*----- initialize stacks and bounds */ fdnpix = 0; @@ -149,7 +149,7 @@ void preanalyse(int no, objliststruct *objlist) fdnpix++; } - obj->fdnpix = (LONG)fdnpix; + obj->fdnpix = fdnpix; obj->fdflux = (float)rv; obj->fdpeak = cpeak; obj->dpeak = peak; @@ -178,7 +178,7 @@ void analyse(int no, objliststruct *objlist, int robust, double gain) xm,ym, xm2,ym2,xym, temp,temp2, theta,pmx2,pmy2, errx2, erry2, errxy, cvar, cvarsum; - int x, y, xmin, ymin, area2, dnpix; + int64_t x, y, xmin, ymin, area2, dnpix; preanalyse(no, objlist); diff --git a/src/aperture.c b/src/aperture.c index 5f49e0f..ca194ad 100644 --- a/src/aperture.c +++ b/src/aperture.c @@ -103,14 +103,14 @@ void sep_ellipse_coeffs(double a, double b, double theta, * parameters x, y, r. xmin is inclusive and xmax is exclusive. * Ensures that box is within image bound and sets a flag if it is not. */ -static void boxextent(double x, double y, double rx, double ry, int w, int h, - int *xmin, int *xmax, int *ymin, int *ymax, +static void boxextent(double x, double y, double rx, double ry, int64_t w, int64_t h, + int64_t *xmin, int64_t *xmax, int64_t *ymin, int64_t *ymax, short *flag) { - *xmin = (int)(x - rx + 0.5); - *xmax = (int)(x + rx + 1.4999999); - *ymin = (int)(y - ry + 0.5); - *ymax = (int)(y + ry + 1.4999999); + *xmin = (int64_t)(x - rx + 0.5); + *xmax = (int64_t)(x + rx + 1.4999999); + *ymin = (int64_t)(y - ry + 0.5); + *ymax = (int64_t)(y + ry + 1.4999999); if (*xmin < 0) { *xmin = 0; @@ -136,8 +136,8 @@ static void boxextent(double x, double y, double rx, double ry, int w, int h, static void boxextent_ellipse(double x, double y, double cxx, double cyy, double cxy, double r, - int w, int h, - int *xmin, int *xmax, int *ymin, int *ymax, + int64_t w, int64_t h, + int64_t *xmin, int64_t *xmax, int64_t *ymin, int64_t *ymax, short *flag) { double dxlim, dylim; @@ -326,7 +326,7 @@ static void oversamp_ann_ellipse(double r, double b, double *r_in2, * that it doesn't quite make sense to use aperture.i. */ int sep_sum_circann_multi(const sep_image *im, - double x, double y, double rmax, int n, + double x, double y, double rmax, int64_t n, int id, int subpix, short inflag, @@ -335,13 +335,13 @@ int sep_sum_circann_multi(const sep_image *im, { PIXTYPE pix, varpix; double dx, dy, dx1, dy2, offset, scale, scale2, tmp, rpix2; - int ix, iy, xmin, xmax, ymin, ymax, sx, sy, status, size, esize, msize, ssize; - long pos; + int64_t ix, iy, xmin, xmax, ymin, ymax, sx, sy, size, esize, msize, ssize, pos; + int status; short errisarray, errisstd; const BYTE *datat, *errort, *maskt, *segt; converter convert, econvert, mconvert, sconvert; double rpix, r_out, r_out2, d, prevbinmargin, nextbinmargin, step, stepdens; - int j, ismasked; + int64_t j, ismasked; /* input checks */ if (rmax < 0.0 || n < 1) @@ -487,7 +487,7 @@ int sep_sum_circann_multi(const sep_image *im, dy2 = dy*dy; for (sx=subpix; sx--; dx1+=scale) { - j = (int)(sqrt(dx1*dx1+dy2)*stepdens); + j = (int64_t)(sqrt(dx1*dx1+dy2)*stepdens); if (j < n) { if (ismasked) @@ -505,7 +505,7 @@ int sep_sum_circann_multi(const sep_image *im, else /* pixel not close to bin boundary */ { - j = (int)(rpix*stepdens); + j = (int64_t)(rpix*stepdens); if (j < n) { if (ismasked) @@ -558,10 +558,10 @@ int sep_sum_circann_multi(const sep_image *im, /* for use in flux_radius */ -static double inverse(double xmax, const double *y, int n, double ytarg) +static double inverse(double xmax, const double *y, int64_t n, double ytarg) { double step; - int i; + int64_t i; step = xmax/n; i = 0; @@ -586,11 +586,11 @@ static double inverse(double xmax, const double *y, int n, double ytarg) int sep_flux_radius(const sep_image *im, double x, double y, double rmax, int id, int subpix, short inflag, - const double *fluxtot, const double *fluxfrac, int n, double *r, + const double *fluxtot, const double *fluxfrac, int64_t n, double *r, short *flag) { int status; - int i; + int64_t i; double f; double sumbuf[FLUX_RADIUS_BUFSIZE] = {0.}; double sumvarbuf[FLUX_RADIUS_BUFSIZE]; @@ -625,8 +625,8 @@ int sep_kron_radius(const sep_image *im, double x, double y, { float pix; double r1, v1, r2, area, rpix2, dx, dy; - int ix, iy, xmin, xmax, ymin, ymax, status, size, msize, ssize; - long pos; + int64_t ix, iy, xmin, xmax, ymin, ymax, pos, size, msize, ssize; + int status; int ismasked; const BYTE *datat, *maskt, *segt; @@ -739,12 +739,12 @@ int sep_kron_radius(const sep_image *im, double x, double y, /* set array values within an ellipse (uc = unsigned char array) */ -void sep_set_ellipse(unsigned char *arr, int w, int h, +void sep_set_ellipse(unsigned char *arr, int64_t w, int64_t h, double x, double y, double cxx, double cyy, double cxy, double r, unsigned char val) { unsigned char *arrt; - int xmin, xmax, ymin, ymax, xi, yi; + int64_t xmin, xmax, ymin, ymax, xi, yi; double r2, dx, dy, dy2; short flag; /* not actually used, but input to boxextent */ @@ -788,8 +788,8 @@ int sep_windowed(const sep_image *im, double maskarea, maskweight, maskdxpos, maskdypos; double r, tv, twv, sigtv, totarea, overlap, rpix2, invtwosig2; double wpix; - int i, ix, iy, xmin, xmax, ymin, ymax, sx, sy, status, size, esize, msize; - long pos; + int64_t i, ix, iy, xmin, xmax, ymin, ymax, sx, sy, pos, size, esize, msize; + int status; short errisarray, errisstd; const BYTE *datat, *errort, *maskt; converter convert, econvert, mconvert; diff --git a/src/aperture.i b/src/aperture.i index fdfcb52..53067e4 100644 --- a/src/aperture.i +++ b/src/aperture.i @@ -16,9 +16,8 @@ int APER_NAME(const sep_image *im, PIXTYPE pix, varpix; double dx, dy, dx1, dy2, offset, scale, scale2, tmp; double tv, sigtv, totarea, maskarea, overlap, rpix2; - int ix, iy, xmin, xmax, ymin, ymax, sx, sy, status, size, esize, msize, ssize; - int ismasked; - long pos; + int64_t ix, iy, xmin, xmax, ymin, ymax, sx, sy, pos, size, esize, msize, ssize; + int ismasked, status; short errisarray, errisstd; const BYTE *datat, *errort, *maskt, *segt; converter convert, econvert, mconvert, sconvert; diff --git a/src/background.c b/src/background.c index cf88e18..64128c1 100644 --- a/src/background.c +++ b/src/background.c @@ -35,41 +35,42 @@ /* Background info in a single mesh*/ typedef struct { float mode, mean, sigma; /* Background mode, mean and sigma */ - LONG *histo; /* Pointer to a histogram */ + int64_t *histo; /* Pointer to a histogram */ int nlevels; /* Nb of histogram bins */ float qzero, qscale; /* Position of histogram */ float lcut, hcut; /* Histogram cuts */ - int npix; /* Number of pixels involved */ + int64_t npix; /* Number of pixels involved */ } backstruct; /* internal helper functions */ void backhisto(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int bufsize, - int n, int w, int bw, PIXTYPE maskthresh); + const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, + int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh); void backstat(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int bufsize, - int n, int w, int bw, PIXTYPE maskthresh); -int filterback(sep_bkg *bkg, int fw, int fh, double fthresh); + const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, + int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh); +int filterback(sep_bkg *bkg, int64_t fw, int64_t fh, double fthresh); float backguess(backstruct *bkg, float *mean, float *sigma); int makebackspline(const sep_bkg *bkg, float *map, float *dmap); -int sep_background(const sep_image* image, int bw, int bh, int fw, int fh, +int sep_background(const sep_image* image, int64_t bw, int64_t bh, int64_t fw, int64_t fh, double fthresh, sep_bkg **bkg) { const BYTE *imt, *maskt; - int npix; /* size of image */ - int nx, ny, nb; /* number of background boxes in x, y, total */ - int bufsize; /* size of a "row" of boxes in pixels (w*bh) */ - int elsize; /* size (in bytes) of an image array element */ - int melsize; /* size (in bytes) of a mask array element */ + int64_t npix; /* size of image */ + int64_t nx, ny, nb; /* number of background boxes in x, y, total */ + int64_t bufsize; /* size of a "row" of boxes in pixels (w*bh) */ + int64_t elsize; /* size (in bytes) of an image array element */ + int64_t melsize; /* size (in bytes) of a mask array element */ PIXTYPE *buf, *mbuf; const PIXTYPE *buft, *mbuft; PIXTYPE maskthresh; array_converter convert, mconvert; backstruct *backmesh, *bm; /* info about each background "box" */ sep_bkg *bkgout; /* output */ - int j,k,m, status; + int64_t j,k,m; + int status; status = RETURN_OK; npix = image->w * image->h; @@ -180,7 +181,7 @@ int sep_background(const sep_image* image, int bw, int bh, int fw, int fh, if (bm->mean <= -BIG) bm->histo=NULL; else - QCALLOC(bm->histo, LONG, bm->nlevels, status); + QCALLOC(bm->histo, int64_t, bm->nlevels, status); backhisto(backmesh, buft, mbuft, bufsize, nx, image->w, bw, maskthresh); /* Compute background statistics from the histograms */ @@ -243,14 +244,14 @@ int sep_background(const sep_image* image, int bw, int bh, int fw, int fh, Compute robust statistical estimators in a row of meshes. */ void backstat(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int bufsize, - int n, int w, int bw, PIXTYPE maskthresh) + const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, + int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh) { backstruct *bm; double pix, wpix, sig, mean, sigma, step; const PIXTYPE *buft,*wbuft; PIXTYPE lcut,hcut; - int m,h,x,y, npix,wnpix, offset, lastbite; + int64_t m,h,x,y, npix,wnpix, offset, lastbite; h = bufsize/w; /* height of background boxes in this row */ bm = backmesh; @@ -362,14 +363,14 @@ void backstat(backstruct *backmesh, Fill histograms in a row of meshes. */ void backhisto(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int bufsize, - int n, int w, int bw, PIXTYPE maskthresh) + const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, + int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh) { backstruct *bm; const PIXTYPE *buft,*wbuft; float qscale, cste, wpix; - LONG *histo; - int h,m,x,y, nlevels, lastbite, offset, bin; + int64_t *histo; + int64_t h,m,x,y, nlevels, lastbite, offset, bin; h = bufsize/w; bm = backmesh; @@ -402,7 +403,7 @@ void backhisto(backstruct *backmesh, for (y=h; y--; buft+=offset, wbuft+=offset) for (x=bw; x--;) { - bin = (int)(*(buft++)/qscale + cste); + bin = (int64_t)(*(buft++)/qscale + cste); if ((wpix = *(wbuft++))<=maskthresh && bin=0) (*(histo+bin))++; } @@ -412,7 +413,7 @@ void backhisto(backstruct *backmesh, for (y=h; y--; buft += offset) for (x=bw; x--;) { - bin = (int)(*(buft++)/qscale + cste); + bin = (int64_t)(*(buft++)/qscale + cste); if (bin>=0 && binmean<=-BIG) @@ -480,8 +481,8 @@ float backguess(backstruct *bkg, float *mean, float *sigma) } sig = sig>0.0?sqrt(sig):0.0; - lcut = (ftemp=med-3.0*sig)>0.0 ?(int)(ftemp>0.0?ftemp+0.5:ftemp-0.5):0; - hcut = (ftemp=med+3.0*sig)0.0?ftemp+0.5:ftemp-0.5) + lcut = (ftemp=med-3.0*sig)>0.0 ?(int64_t)(ftemp>0.0?ftemp+0.5:ftemp-0.5):0; + hcut = (ftemp=med+3.0*sig)0.0?ftemp+0.5:ftemp-0.5) : nlevelsm1; } @@ -499,13 +500,13 @@ float backguess(backstruct *bkg, float *mean, float *sigma) /****************************************************************************/ -int filterback(sep_bkg *bkg, int fw, int fh, double fthresh) +int filterback(sep_bkg *bkg, int64_t fw, int64_t fh, double fthresh) /* Median filterthe background map to remove the contribution * from bright sources. */ { float *back, *sigma, *back2, *sigma2, *bmask, *smask, *sigmat; float d2, d2min, med, val, sval; - int i, j, px, py, np, nx, ny, npx, npx2, npy, npy2, dpx, dpy, x, y, nmin; + int64_t i, j, px, py, np, nx, ny, npx, npx2, npy, npy2, dpx, dpy, x, y, nmin; int status; status = RETURN_OK; @@ -638,7 +639,8 @@ int filterback(sep_bkg *bkg, int fw, int fh, double fthresh) */ int makebackspline(const sep_bkg *bkg, float *map, float *dmap) { - int x, y, nbx, nby, nbym1, status; + int64_t x, y, nbx, nby, nbym1; + int status; float *dmapt, *mapt, *u, temp; u = NULL; status = RETURN_OK; @@ -698,13 +700,13 @@ float sep_bkg_globalrms(const sep_bkg *bkg) /*****************************************************************************/ -float sep_bkg_pix(const sep_bkg *bkg, int x, int y) +float sep_bkg_pix(const sep_bkg *bkg, int64_t x, int64_t y) /* * return background at position x,y. * (linear interpolation between background map vertices). */ { - int nx, ny, xl, yl, pos; + int64_t nx, ny, xl, yl, pos; double dx, dy, cdx; float *b; float b0, b1, b2, b3; @@ -715,8 +717,8 @@ float sep_bkg_pix(const sep_bkg *bkg, int x, int y) dx = (double)x/bkg->bw - 0.5; dy = (double)y/bkg->bh - 0.5; - dx -= (xl = (int)dx); - dy -= (yl = (int)dy); + dx -= (xl = (int64_t)dx); + dy -= (yl = (int64_t)dy); if (xl<0) { @@ -754,7 +756,7 @@ float sep_bkg_pix(const sep_bkg *bkg, int x, int y) /*****************************************************************************/ -int bkg_line_flt_internal(const sep_bkg *bkg, float *values, float *dvalues, int y, +int bkg_line_flt_internal(const sep_bkg *bkg, float *values, float *dvalues, int64_t y, float *line) /* Interpolate background at line y (bicubic spline interpolation between * background map vertices) and save to line. @@ -762,7 +764,8 @@ int bkg_line_flt_internal(const sep_bkg *bkg, float *values, float *dvalues, int * (bkg->sigma, bkg->dsigma) depending on whether the background value or rms * is being evaluated. */ { - int i,j,x,yl, nbx,nbxm1,nby, nx,width, ystep, changepoint, status; + int64_t i,j,x,yl, nbx,nbxm1,nby, nx,width, ystep, changepoint; + int status; float dx,dx0,dy,dy3, cdx,cdy,cdy3, temp, xstep; float *nodebuf, *dnodebuf, *u; float *node, *nodep, *dnode, *blo, *bhi, *dblo, *dbhi; @@ -779,7 +782,7 @@ int bkg_line_flt_internal(const sep_bkg *bkg, float *values, float *dvalues, int if (nby > 1) { dy = (float)y/bkg->bh - 0.5; - dy -= (yl = (int)dy); + dy -= (yl = (int64_t)dy); if (yl<0) { yl = 0; @@ -885,7 +888,7 @@ int bkg_line_flt_internal(const sep_bkg *bkg, float *values, float *dvalues, int return status; } -int sep_bkg_line_flt(const sep_bkg *bkg, int y, float *line) +int sep_bkg_line_flt(const sep_bkg *bkg, int64_t y, float *line) /* Interpolate background at line y (bicubic spline interpolation between * background map vertices) and save to line */ { @@ -894,7 +897,7 @@ int sep_bkg_line_flt(const sep_bkg *bkg, int y, float *line) /*****************************************************************************/ -int sep_bkg_rmsline_flt(const sep_bkg *bkg, int y, float *line) +int sep_bkg_rmsline_flt(const sep_bkg *bkg, int64_t y, float *line) /* Interpolate background rms at line y (bicubic spline interpolation between * background map vertices) and save to line */ { @@ -905,10 +908,11 @@ int sep_bkg_rmsline_flt(const sep_bkg *bkg, int y, float *line) /* Multiple dtype functions and convenience functions. * These mostly wrap the two "line" functions above. */ -int sep_bkg_line(const sep_bkg *bkg, int y, void *line, int dtype) +int sep_bkg_line(const sep_bkg *bkg, int64_t y, void *line, int dtype) { array_writer write_array; - int size, status; + int64_t size; + int status; float *tmpline; if (dtype == SEP_TFLOAT) @@ -933,10 +937,11 @@ int sep_bkg_line(const sep_bkg *bkg, int y, void *line, int dtype) return status; } -int sep_bkg_rmsline(const sep_bkg *bkg, int y, void *line, int dtype) +int sep_bkg_rmsline(const sep_bkg *bkg, int64_t y, void *line, int dtype) { array_writer write_array; - int size, status; + int64_t size; + int status; float *tmpline; if (dtype == SEP_TFLOAT) @@ -963,7 +968,8 @@ int sep_bkg_rmsline(const sep_bkg *bkg, int y, void *line, int dtype) int sep_bkg_array(const sep_bkg *bkg, void *arr, int dtype) { - int y, width, size, status; + int64_t y, width, size; + int status; array_writer write_array; float *tmpline; BYTE *line; @@ -1001,7 +1007,8 @@ int sep_bkg_array(const sep_bkg *bkg, void *arr, int dtype) int sep_bkg_rmsarray(const sep_bkg *bkg, void *arr, int dtype) { - int y, width, size, status; + int64_t y, width, size; + int status; array_writer write_array; float *tmpline; BYTE *line; @@ -1037,10 +1044,11 @@ int sep_bkg_rmsarray(const sep_bkg *bkg, void *arr, int dtype) return status; } -int sep_bkg_subline(const sep_bkg *bkg, int y, void *line, int dtype) +int sep_bkg_subline(const sep_bkg *bkg, int64_t y, void *line, int dtype) { array_writer subtract_array; - int status, size; + int status; + int64_t size; PIXTYPE *tmpline; tmpline = NULL; @@ -1066,7 +1074,8 @@ int sep_bkg_subline(const sep_bkg *bkg, int y, void *line, int dtype) int sep_bkg_subarray(const sep_bkg *bkg, void *arr, int dtype) { array_writer subtract_array; - int y, status, size, width; + int64_t y, size, width; + int status; PIXTYPE *tmpline; BYTE *arrt; diff --git a/src/convolve.c b/src/convolve.c index 9b209cb..fb27998 100644 --- a/src/convolve.c +++ b/src/convolve.c @@ -36,10 +36,10 @@ f* GNU Lesser General Public License for more details. * convw, convh : width and height of conv * buf : output convolved line (buf->dw elements long) */ -int convolve(arraybuffer *buf, int y, const float *conv, int convw, int convh, - PIXTYPE *out) +int convolve(arraybuffer *buf, int64_t y, const float *conv, + int64_t convw, int64_t convh, PIXTYPE *out) { - int convw2, convn, cx, cy, i, dcx, y0; + int64_t convw2, convn, cx, cy, i, dcx, y0; PIXTYPE *line; /* current line in input buffer */ PIXTYPE *outend; /* end of output buffer */ PIXTYPE *src, *dst, *dstend; @@ -120,11 +120,11 @@ int convolve(arraybuffer *buf, int y, const float *conv, int convw, int convh, * imbuf and nbuf should have same data dimensions and be on the same line * (their `yoff` fields should be the same). */ -int matched_filter(arraybuffer *imbuf, arraybuffer *nbuf, int y, - const float *conv, int convw, int convh, +int matched_filter(arraybuffer *imbuf, arraybuffer *nbuf, int64_t y, + const float *conv, int64_t convw, int64_t convh, PIXTYPE *work, PIXTYPE *out, int noise_type) { - int convw2, convn, cx, cy, i, dcx, y0; + int64_t convw2, convn, cx, cy, i, dcx, y0; PIXTYPE imval, varval; PIXTYPE *imline, *nline; /* current line in input buffer */ PIXTYPE *outend; /* end of output buffer */ diff --git a/src/deblend.c b/src/deblend.c index af3f87c..945f18b 100644 --- a/src/deblend.c +++ b/src/deblend.c @@ -48,7 +48,7 @@ int sep_get_sub_object_limit() int belong(int, objliststruct *, int, objliststruct *); -int *createsubmap(objliststruct *, int, int *, int *, int *, int *); +int64_t *createsubmap(objliststruct *, int64_t, int64_t *, int64_t *, int64_t *, int64_t *); int gatherup(objliststruct *, objliststruct *); /******************************** deblend ************************************/ @@ -59,18 +59,16 @@ NOTE: Even if the object is not deblended, the output objlist threshold is This can return two error codes: DEBLEND_OVERFLOW or MEMORY_ALLOC_ERROR */ -int deblend(objliststruct *objlistin, int l, objliststruct *objlistout, +int deblend(objliststruct *objlistin, int64_t l, objliststruct *objlistout, int deblend_nthresh, double deblend_mincont, int minarea, deblendctx *ctx) { objstruct *obj; objliststruct debobjlist, debobjlist2; double thresh, thresh0, value0; - int h,i,j,k,m,subx,suby,subh,subw, - xn, - nbm = NBRANCH, - status; - int *submap; + int64_t h,i,j,k,m,subx,suby,subh,subw,xn, nbm = NBRANCH; + int64_t *submap; + int status; submap = NULL; status = RETURN_OK; @@ -163,7 +161,7 @@ int deblend(objliststruct *objlistin, int l, objliststruct *objlistout, obj = objlist[k+1].obj; for (i=0; ison[k+xn*(i+nsonmax*h)])!=-1; h++) + for (m=h=0; (j=(int64_t)ctx->son[k+xn*(i+nsonmax*h)])!=-1; h++) { if (obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) m++; @@ -171,7 +169,7 @@ int deblend(objliststruct *objlistin, int l, objliststruct *objlistout, } if (m>1) { - for (h=0; (j=(int)ctx->son[k+xn*(i+nsonmax*h)])!=-1; h++) + for (h=0; (j=(int64_t)ctx->son[k+xn*(i+nsonmax*h)])!=-1; h++) if (ctx->ok[k+1+xn*j] && obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) { @@ -218,7 +216,7 @@ int deblend(objliststruct *objlistin, int l, objliststruct *objlistout, /* Allocate the memory allocated by global pointers in refine.c */ -int allocdeblend(int deblend_nthresh, int w, int h, deblendctx *ctx) +int allocdeblend(int deblend_nthresh, int64_t w, int64_t h, deblendctx *ctx) { int status=RETURN_OK; memset(ctx, 0, sizeof(deblendctx)); @@ -263,8 +261,9 @@ int gatherup(objliststruct *objlistin, objliststruct *objlistout) pliststruct *pixelin = objlistin->plist, *pixelout, *pixt,*pixt2; - int i,k,l, *n, iclst, npix, bmwidth, - nobj = objlistin->nobj, xs,ys, x,y, status; + int64_t i,k,l, *n, iclst, npix, bmwidth, + nobj = objlistin->nobj, xs,ys, x,y; + int status; bmp = NULL; amp = p = NULL; @@ -275,7 +274,7 @@ int gatherup(objliststruct *objlistin, objliststruct *objlistout) QMALLOC(amp, float, nobj, status); QMALLOC(p, float, nobj, status); - QMALLOC(n, int, nobj, status); + QMALLOC(n, int64_t, nobj, status); for (i=1; iobj[shellnb]); pliststruct *cpl = coreobjlist->plist, *spl = shellobjlist->plist, *pixt; - int xc=PLIST(cpl+cobj->firstpix,x), yc=PLIST(cpl+cobj->firstpix,y); + int64_t xc=PLIST(cpl+cobj->firstpix,x), yc=PLIST(cpl+cobj->firstpix,y); for (pixt = spl+sobj->firstpix; pixt>=spl; pixt = spl+PLIST(pixt,nextpix)) if ((PLIST(pixt,x) == xc) && (PLIST(pixt,y) == yc)) @@ -402,12 +401,12 @@ int belong(int corenb, objliststruct *coreobjlist, /* Create pixel-index submap for deblending. */ -int *createsubmap(objliststruct *objlistin, int no, - int *subx, int *suby, int *subw, int *subh) +int64_t *createsubmap(objliststruct *objlistin, int64_t no, + int64_t *subx, int64_t *suby, int64_t *subw, int64_t *subh) { objstruct *obj; pliststruct *pixel, *pixt; - int i, n, xmin,ymin, w, *pix, *pt, *submap; + int64_t i, n, xmin,ymin, w, *pix, *pt, *submap; obj = objlistin->obj+no; pixel = objlistin->plist; @@ -418,7 +417,7 @@ int *createsubmap(objliststruct *objlistin, int no, *subh = obj->ymax - ymin + 1; n = w**subh; - if (!(submap = pix = malloc(n*sizeof(int)))) + if (!(submap = pix = malloc(n*sizeof(int64_t)))) return NULL; pt = pix; for (i=n; i--;) diff --git a/src/extract.c b/src/extract.c index 09b4408..40707a8 100644 --- a/src/extract.c +++ b/src/extract.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "sep.h" #include "sepcore.h" @@ -35,9 +36,9 @@ /* thresholding filtered weight-maps */ /* globals */ -_Thread_local int plistexist_cdvalue, plistexist_thresh, plistexist_var; -_Thread_local int plistoff_value, plistoff_cdvalue, plistoff_thresh, plistoff_var; -_Thread_local int plistsize; +_Thread_local int64_t plistexist_cdvalue, plistexist_thresh, plistexist_var; +_Thread_local int64_t plistoff_value, plistoff_cdvalue, plistoff_thresh, plistoff_var; +_Thread_local int64_t plistsize; _Thread_local unsigned int randseed; static _Atomic size_t extract_pixstack = 300000; @@ -60,10 +61,10 @@ int segsortit(infostruct *info, objliststruct *objlist, void plistinit(int hasconv, int hasvar); void clean(objliststruct *objlist, double clean_param, int *survives); int convert_to_catalog(objliststruct *objlist, const int *survives, - sep_catalog *cat, int w, int include_pixels); + sep_catalog *cat, int64_t w, int include_pixels); -int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, int w, int h, - int bufw, int bufh); +int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, + int64_t w, int64_t h, int64_t bufw, int64_t bufh); void arraybuffer_readline(arraybuffer *buf); void arraybuffer_free(arraybuffer *buf); @@ -71,9 +72,10 @@ void arraybuffer_free(arraybuffer *buf); /* initialize buffer */ /* bufw must be less than or equal to w */ -int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, int w, int h, - int bufw, int bufh) { - int status, yl; +int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, + int64_t w, int64_t h, int64_t bufw, int64_t bufh) { + int status; + int64_t yl; status = RETURN_OK; /* data info */ @@ -113,7 +115,7 @@ int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, int w, int h, /* read a line into the buffer at the top, shifting all lines down one */ void arraybuffer_readline(arraybuffer *buf) { PIXTYPE *line; - int y; + int64_t y; /* shift all lines down one */ for (line = buf->bptr; line < buf->lastline; line += buf->bw) @@ -152,7 +154,7 @@ void arraybuffer_free(arraybuffer *buf) { * infinity in the noise buffer (if present). It affects the first */ void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf) { - int i; + int64_t i; for (i=0; ibw; i++) { if (mbuf->lastline[i] > 0.0) { @@ -166,7 +168,7 @@ void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf) { /****************************** extract **************************************/ int sep_extract(const sep_image *image, float thresh, int thresh_type, - int minarea, const float *conv, int convw, int convh, + int minarea, const float *conv, int64_t convw, int64_t convh, int filter_type, int deblend_nthresh, double deblend_cont, int clean_flag, double clean_param, sep_catalog **catalog) { @@ -175,14 +177,13 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, objliststruct objlist; char newmarker; size_t mem_pixstack; - int nposize, oldnposize; - int w, h; - int co, i, j, luflag, pstop, xl, xl2, yl, cn; - int ididx, numids, totnpix; - long prevpix; - int stacksize, convn, status; - int bufh; - int isvarthresh, isvarnoise; + int64_t nposize, oldnposize; + int64_t w, h; + int64_t co, i, j, pstop, xl, xl2, yl, cn; + int64_t ididx, numids, totnpix; + int64_t prevpix, bufh; + int64_t stacksize, convn; + int status, isvarthresh, isvarnoise, luflag; short trunflag; PIXTYPE relthresh, cdnewsymbol, pixvar, pixsig; float sum; @@ -190,12 +191,13 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, infostruct *info, *store; objliststruct *finalobjlist; - pliststruct *pixel, *pixt; + pliststruct *pixel, *pixt; char *marker; PIXTYPE *scan, *cdscan, *wscan, *dummyscan, *sscan; PIXTYPE *sigscan, *workscan; float *convnorm; - int *start, *end, *survives; + int64_t *start, *end, *cumcounts; + int *survives; pixstatus *psstack; char errtext[512]; sep_catalog *cat; @@ -220,6 +222,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, h = image->h; numids = (image->numids) ? image->numids : 1; infostruct idinfo[numids]; + prevpix = 0; isvarthresh = 0; relthresh = 0.0; pixvar = 0.0; @@ -230,8 +233,10 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, mem_pixstack = sep_get_extract_pixstack(); if (image->segmap) { + QCALLOC(cumcounts, int64_t, numids, status) totnpix = 0; for (i=0; iidcounts[i]; } if (totnpix>mem_pixstack) { @@ -294,8 +299,8 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, QMALLOC(marker, char, stacksize, status); QMALLOC(dummyscan, PIXTYPE, stacksize, status); QMALLOC(psstack, pixstatus, stacksize, status); - QCALLOC(start, int, stacksize, status); - QMALLOC(end, int, stacksize, status); + QCALLOC(start, int64_t, stacksize, status); + QMALLOC(end, int64_t, stacksize, status); if ((status = allocdeblend(deblend_nthresh, w, h, &deblendctx)) != RETURN_OK) goto exit; @@ -358,8 +363,8 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, } else { objlist.nobj = 1; } - curpixinfo.pixnb = 1; - + curpixinfo.pixnb = 1; + /* Init finalobjlist */ QMALLOC(finalobjlist, objliststruct, 1, status); finalobjlist->obj = NULL; @@ -498,11 +503,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (xl == 0 || xl == w - 1) curpixinfo.flag |= SEP_OBJ_TRUNC; - prevpix = 0; for (ididx=0; ididxsegids[ididx]==(int)sscan[xl]) { + if (image->segids[ididx]==(long)sscan[xl]) { - prevpix += idinfo[ididx].pixnb; + pixt = pixel + prevpix*plistsize; + prevpix = cumcounts[ididx] + idinfo[ididx].pixnb; pixt = pixel + prevpix*plistsize; PLIST(pixt, x) = xl; @@ -530,9 +535,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, idinfo[ididx].pixnb++; }; break; - } else { - prevpix += image->idcounts[ididx]; - } + } } } } else { @@ -671,7 +674,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, ps = psstack[--pstop]; if ((cs == NONOBJECT) && (ps == COMPLETE)) { if (start[co] == UNKNOWN) { - if ((int)info[co].pixnb >= minarea) { + if ((int64_t)info[co].pixnb >= minarea) { /* update threshold before object is processed */ objlist.thresh = thresh; @@ -748,6 +751,10 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, free(finalobjlist->plist); free(finalobjlist); } + if (image->segmap) { + free(cumcounts); + arraybuffer_free(&sbuf); + } freedeblend(&deblendctx); free(pixel); free(info); @@ -788,7 +795,8 @@ int segsortit(infostruct *info, objliststruct *objlist, objliststruct *finalobjlist, double gain) { objliststruct objlistout, *objlist2; objstruct obj; - int i, status; + int64_t i; + int status; status = RETURN_OK; @@ -826,7 +834,8 @@ int sortit(infostruct *info, objliststruct *objlist, int minarea, { objliststruct objlistout, *objlist2; objstruct obj; - int i, status; + int64_t i; + int status; status=RETURN_OK; objlistout.obj = NULL; @@ -894,7 +903,7 @@ int addobjdeep(int objnb, objliststruct *objl1, objliststruct *objl2) { objstruct *objl2obj; pliststruct *plist1 = objl1->plist, *plist2 = objl2->plist; - int fp, i, j, npx, objnb2; + int64_t fp, i, j, npx, objnb2; fp = objl2->npix; /* 2nd list's plist size in pixels */ j = fp*plistsize; /* 2nd list's plist size in bytes */ @@ -1000,7 +1009,7 @@ Fill a list with whether each object in the list survived the cleaning void clean(objliststruct *objlist, double clean_param, int *survives) { objstruct *obj1, *obj2; - int i,j; + int64_t i,j; double amp,ampin,alpha,alphain, unitarea,unitareain,beta,val; float dx,dy,rlim; @@ -1117,10 +1126,10 @@ void free_catalog_fields(sep_catalog *catalog) * `w`: width of image (used to calculate linear indicies). */ int convert_to_catalog(objliststruct *objlist, const int *survives, - sep_catalog *cat, int w, int include_pixels) + sep_catalog *cat, int64_t w, int include_pixels) { - int i, j, k; - int totnpix; + int64_t i, j, k; + int64_t totnpix; int nobj = 0; int status = RETURN_OK; objstruct *obj; @@ -1141,12 +1150,12 @@ int convert_to_catalog(objliststruct *objlist, const int *survives, /* allocate catalog fields */ cat->nobj = nobj; QMALLOC(cat->thresh, float, nobj, status); - QMALLOC(cat->npix, int, nobj, status); - QMALLOC(cat->tnpix, int, nobj, status); - QMALLOC(cat->xmin, int, nobj, status); - QMALLOC(cat->xmax, int, nobj, status); - QMALLOC(cat->ymin, int, nobj, status); - QMALLOC(cat->ymax, int, nobj, status); + QMALLOC(cat->npix, int64_t, nobj, status); + QMALLOC(cat->tnpix, int64_t, nobj, status); + QMALLOC(cat->xmin, int64_t, nobj, status); + QMALLOC(cat->xmax, int64_t, nobj, status); + QMALLOC(cat->ymin, int64_t, nobj, status); + QMALLOC(cat->ymax, int64_t, nobj, status); QMALLOC(cat->x, double, nobj, status); QMALLOC(cat->y, double, nobj, status); QMALLOC(cat->x2, double, nobj, status); @@ -1165,10 +1174,10 @@ int convert_to_catalog(objliststruct *objlist, const int *survives, QMALLOC(cat->flux, float, nobj, status); QMALLOC(cat->cpeak, float, nobj, status); QMALLOC(cat->peak, float, nobj, status); - QMALLOC(cat->xcpeak, int, nobj, status); - QMALLOC(cat->ycpeak, int, nobj, status); - QMALLOC(cat->xpeak, int, nobj, status); - QMALLOC(cat->ypeak, int, nobj, status); + QMALLOC(cat->xcpeak, int64_t, nobj, status); + QMALLOC(cat->ycpeak, int64_t, nobj, status); + QMALLOC(cat->xpeak, int64_t, nobj, status); + QMALLOC(cat->ypeak, int64_t, nobj, status); QMALLOC(cat->cflux, float, nobj, status); QMALLOC(cat->flux, float, nobj, status); QMALLOC(cat->flag, short, nobj, status); @@ -1227,10 +1236,10 @@ int convert_to_catalog(objliststruct *objlist, const int *survives, for (i=0; inobj; i++) totnpix += cat->npix[i]; /* allocate buffer for all objects' pixels */ - QMALLOC(cat->objectspix, int, totnpix, status); + QMALLOC(cat->objectspix, int64_t, totnpix, status); /* allocate array of pointers into the above buffer */ - QMALLOC(cat->pix, int*, nobj, status); + QMALLOC(cat->pix, int64_t*, nobj, status); pixel = objlist->plist; diff --git a/src/extract.h b/src/extract.h index c40bb85..ec0d73e 100644 --- a/src/extract.h +++ b/src/extract.h @@ -42,9 +42,9 @@ typedef enum {COMPLETE, INCOMPLETE, NONOBJECT, OBJECT} pixstatus; /* Temporary object parameters during extraction */ typedef struct structinfo { - long pixnb; /* Number of pixels included */ - long firstpix; /* Pointer to first pixel of pixlist */ - long lastpix; /* Pointer to last pixel of pixlist */ + int64_t pixnb; /* Number of pixels included */ + int64_t firstpix; /* Pointer to first pixel of pixlist */ + int64_t lastpix; /* Pointer to last pixel of pixlist */ short flag; /* Extraction flag */ } infostruct; @@ -52,8 +52,8 @@ typedef char pliststruct; /* Dummy type for plist */ typedef struct { - int nextpix; - int x, y; + int64_t nextpix; + int64_t x, y; PIXTYPE value; } pbliststruct; @@ -62,22 +62,22 @@ typedef struct { const BYTE *dptr; /* pointer to original data, can be any supported type */ int dtype; /* data type of original data */ - int dw, dh; /* original data width, height */ + int64_t dw, dh; /* original data width, height */ PIXTYPE *bptr; /* buffer pointer (self-managed memory) */ - int bw, bh; /* buffer width, height (bufw can be larger than w due */ + int64_t bw, bh; /* buffer width, height (bufw can be larger than w due */ /* to padding). */ PIXTYPE *midline; /* "middle" line in buffer (at index bh/2) */ PIXTYPE *lastline; /* last line in buffer */ array_converter readline; /* function to read a data line into buffer */ - int elsize; /* size in bytes of one element in original data */ - int yoff; /* line index in original data corresponding to bufptr */ + int64_t elsize; /* size in bytes of one element in original data */ + int64_t yoff; /* line index in original data corresponding to bufptr */ } arraybuffer; /* globals */ -extern _Thread_local int plistexist_cdvalue, plistexist_thresh, plistexist_var; -extern _Thread_local int plistoff_value, plistoff_cdvalue, plistoff_thresh, plistoff_var; -extern _Thread_local int plistsize; +extern _Thread_local int64_t plistexist_cdvalue, plistexist_thresh, plistexist_var; +extern _Thread_local int64_t plistoff_value, plistoff_cdvalue, plistoff_thresh, plistoff_var; +extern _Thread_local int64_t plistsize; extern _Thread_local unsigned int randseed; typedef struct @@ -87,17 +87,17 @@ typedef struct float mthresh; /* max. threshold (ADU) */ /* # pixels */ - int fdnpix; /* nb of extracted pix */ - int dnpix; /* nb of pix above thresh */ - int npix; /* "" in measured frame */ - int nzdwpix; /* nb of zero-dweights around */ - int nzwpix; /* nb of zero-weights inside */ + int64_t fdnpix; /* nb of extracted pix */ + int64_t dnpix; /* nb of pix above thresh */ + int64_t npix; /* "" in measured frame */ + int64_t nzdwpix; /* nb of zero-dweights around */ + int64_t nzwpix; /* nb of zero-weights inside */ /* position */ - int xpeak, ypeak; /* pos of brightest pix */ - int xcpeak,ycpeak; /* pos of brightest pix */ + int64_t xpeak, ypeak; /* pos of brightest pix */ + int64_t xcpeak,ycpeak; /* pos of brightest pix */ double mx, my; /* barycenter */ - int xmin,xmax,ymin,ymax,ycmin,ycmax; /* x,y limits */ + int64_t xmin,xmax,ymin,ymax,ycmin,ycmax; /* x,y limits */ /* shape */ double mx2,my2,mxy; /* variances and covariance */ @@ -118,15 +118,15 @@ typedef struct short flag; /* extraction flags */ /* accessing individual pixels in plist*/ - int firstpix; /* ptr to first pixel */ - int lastpix; /* ptr to last pixel */ + int64_t firstpix; /* ptr to first pixel */ + int64_t lastpix; /* ptr to last pixel */ } objstruct; typedef struct { - int nobj; /* number of objects in list */ + int64_t nobj; /* number of objects in list */ objstruct *obj; /* pointer to the object array */ - int npix; /* number of pixels in pixel-list */ + int64_t npix; /* number of pixels in pixel-list */ pliststruct *plist; /* pointer to the pixel-list */ PIXTYPE thresh; /* detection threshold */ } objliststruct; @@ -141,14 +141,14 @@ typedef struct { infostruct *info, *store; char *marker; pixstatus *psstack; - int *start, *end, *discan; - int xmin, ymin, xmax, ymax; + int64_t *start, *end, *discan; + int64_t xmin, ymin, xmax, ymax; } lutzbuffers; -int lutzalloc(int, int, lutzbuffers *); +int lutzalloc(int64_t, int64_t, lutzbuffers *); void lutzfree(lutzbuffers *); int lutz(pliststruct *plistin, - int *objrootsubmap, int subx, int suby, int subw, + int64_t *objrootsubmap, int64_t subx, int64_t suby, int64_t subw, objstruct *objparent, objliststruct *objlist, int minarea, lutzbuffers *buffers); @@ -160,9 +160,9 @@ typedef struct { lutzbuffers lutz; } deblendctx; -int allocdeblend(int deblend_nthresh, int w, int h, deblendctx *); +int allocdeblend(int deblend_nthresh, int64_t w, int64_t h, deblendctx *); void freedeblend(deblendctx *); -int deblend(objliststruct *, int, objliststruct *, int, double, int, deblendctx *); +int deblend(objliststruct *, int64_t, objliststruct *, int, double, int, deblendctx *); /*int addobjshallow(objstruct *, objliststruct *); int rmobjshallow(int, objliststruct *); @@ -170,8 +170,8 @@ void mergeobjshallow(objstruct *, objstruct *); */ int addobjdeep(int, objliststruct *, objliststruct *); -int convolve(arraybuffer *buf, int y, const float *conv, int convw, int convh, +int convolve(arraybuffer *buf, int64_t y, const float *conv, int64_t convw, int64_t convh, PIXTYPE *out); -int matched_filter(arraybuffer *imbuf, arraybuffer *nbuf, int y, - const float *conv, int convw, int convh, +int matched_filter(arraybuffer *imbuf, arraybuffer *nbuf, int64_t y, + const float *conv, int64_t convw, int64_t convh, PIXTYPE *work, PIXTYPE *out, int noise_type); diff --git a/src/lutz.c b/src/lutz.c index 8e58183..93bd763 100644 --- a/src/lutz.c +++ b/src/lutz.c @@ -38,10 +38,11 @@ void lutzsort(infostruct *, objliststruct *); /* Allocate once for all memory space for buffers used by lutz(). */ -int lutzalloc(int width, int height, lutzbuffers *buffers) +int lutzalloc(int64_t width, int64_t height, lutzbuffers *buffers) { - int *discant; - int stacksize, i, status=RETURN_OK; + int64_t *discant; + int64_t stacksize, i; + int status=RETURN_OK; memset(buffers, 0, sizeof(lutzbuffers)); @@ -53,9 +54,9 @@ int lutzalloc(int width, int height, lutzbuffers *buffers) QMALLOC(buffers->store, infostruct, stacksize, status); QMALLOC(buffers->marker, char, stacksize, status); QMALLOC(buffers->psstack, pixstatus, stacksize, status); - QMALLOC(buffers->start, int, stacksize, status); - QMALLOC(buffers->end, int, stacksize, status); - QMALLOC(buffers->discan, int, stacksize, status); + QMALLOC(buffers->start, int64_t, stacksize, status); + QMALLOC(buffers->end, int64_t, stacksize, status); + QMALLOC(buffers->discan, int64_t, stacksize, status); discant = buffers->discan; for (i=stacksize; i--;) *(discant++) = -1; @@ -101,20 +102,20 @@ C implementation of R.K LUTZ' algorithm for the extraction of 8-connected pi- xels in an image */ int lutz(pliststruct *plistin, - int *objrootsubmap, int subx, int suby, int subw, + int64_t *objrootsubmap, int64_t subx, int64_t suby, int64_t subw, objstruct *objparent, objliststruct *objlist, int minarea, lutzbuffers *buffers) { infostruct curpixinfo; objstruct *obj; - pliststruct *plist,*pixel, *plistint; + pliststruct *plist,*pixel, *plistint; char newmarker; - int cn, co, luflag, pstop, xl,xl2,yl, - out, deb_maxarea, stx,sty,enx,eny, step, - nobjm = NOBJ, - inewsymbol, *iscan; - short trunflag; + int64_t cn, co, luflag, pstop, xl,xl2,yl, + out, deb_maxarea, stx,sty,enx,eny, step, + nobjm = NOBJ, + inewsymbol, *iscan; + short trunflag; PIXTYPE thresh; pixstatus cs, ps; @@ -259,7 +260,7 @@ int lutz(pliststruct *plistin, { if (buffers->start[co] == UNKNOWN) { - if ((int)buffers->info[co].pixnb >= deb_maxarea) + if ((int64_t)buffers->info[co].pixnb >= deb_maxarea) { if (objlist->nobj>=nobjm) if (!(obj = objlist->obj = (objstruct *) diff --git a/src/sep.h b/src/sep.h index 5e41a66..1615440 100644 --- a/src/sep.h +++ b/src/sep.h @@ -74,11 +74,11 @@ typedef struct { int ndtype; /* element type of noise */ int mdtype; /* element type of mask */ int sdtype; /* element type of segmap */ - int *segids; /* unique ids in segmap */ - int *idcounts; /* counts of unique ids in segmap */ - int numids; /* total number of unique ids in segmap */ - int w; /* array width */ - int h; /* array height */ + int64_t *segids; /* unique ids in segmap */ + int64_t *idcounts; /* counts of unique ids in segmap */ + int64_t numids; /* total number of unique ids in segmap */ + int64_t w; /* array width */ + int64_t h; /* array height */ double noiseval; /* scalar noise value; used only if noise == NULL */ short noise_type; /* interpretation of noise value */ double gain; /* (poisson counts / data unit) */ @@ -91,10 +91,10 @@ typedef struct { * and its noise with splines. */ typedef struct { - int w, h; /* original image width, height */ - int bw, bh; /* single tile width, height */ - int nx, ny; /* number of tiles in x, y */ - int n; /* nx*ny */ + int64_t w, h; /* original image width, height */ + int64_t bw, bh; /* single tile width, height */ + int64_t nx, ny; /* number of tiles in x, y */ + int64_t n; /* nx*ny */ float global; /* global mean */ float globalrms; /* global sigma */ float *back; /* node data for interpolation */ @@ -111,10 +111,10 @@ typedef struct { typedef struct { int nobj; /* number of objects (length of all arrays) */ float *thresh; /* threshold (ADU) */ - int *npix; /* # pixels extracted (size of pix array) */ - int *tnpix; /* # pixels above thresh (unconvolved) */ - int *xmin, *xmax; - int *ymin, *ymax; + int64_t *npix; /* # pixels extracted (size of pix array) */ + int64_t *tnpix; /* # pixels above thresh (unconvolved) */ + int64_t *xmin, *xmax; + int64_t *ymin, *ymax; double *x, *y; /* barycenter (first moments) */ double *x2, *y2, *xy; /* second moments */ double *errx2, *erry2, *errxy; /* second moment errors */ @@ -124,13 +124,13 @@ typedef struct { float *flux; /* total flux of pixels (unconvolved) */ float *cpeak; /* peak intensity (ADU) (convolved) */ float *peak; /* peak intensity (ADU) (unconvolved) */ - int *xcpeak, *ycpeak; /* x, y coords of peak (convolved) pixel */ - int *xpeak, *ypeak; /* x, y coords of peak (unconvolved) pixel */ + int64_t *xcpeak, *ycpeak; /* x, y coords of peak (convolved) pixel */ + int64_t *xpeak, *ypeak; /* x, y coords of peak (unconvolved) pixel */ short *flag; /* extraction flags */ - int **pix; /* array giving indicies of object's pixels in */ + int64_t **pix; /* array giving indicies of object's pixels in */ /* image (linearly indexed). Length is `npix`. */ /* (pointer to within the `objectspix` buffer) */ - int *objectspix; /* buffer holding pixel indicies for all objects */ + int64_t *objectspix; /* buffer holding pixel indicies for all objects */ } sep_catalog; @@ -153,8 +153,8 @@ typedef struct { * - fthresh = 0.0 */ SEP_API int sep_background(const sep_image *image, - int bw, int bh, /* size of a single background tile */ - int fw, int fh, /* filter size in tiles */ + int64_t bw, int64_t bh, /* size of a single background tile */ + int64_t fw, int64_t fh, /* filter size in tiles */ double fthresh, /* filter threshold */ sep_bkg **bkg); /* OUTPUT */ @@ -172,7 +172,7 @@ SEP_API float sep_bkg_globalrms(const sep_bkg *bkg); * Return background at (x, y). * Unlike other routines, this uses simple linear interpolation. */ -SEP_API float sep_bkg_pix(const sep_bkg *bkg, int x, int y); +SEP_API float sep_bkg_pix(const sep_bkg *bkg, int64_t x, int64_t y); /* sep_bkg_[sub,rms]line() @@ -182,9 +182,9 @@ SEP_API float sep_bkg_pix(const sep_bkg *bkg, int x, int y); * The second function subtracts the background from the input array. * Line must be an array with same width as original image. */ -SEP_API int sep_bkg_line(const sep_bkg *bkg, int y, void *line, int dtype); -SEP_API int sep_bkg_subline(const sep_bkg *bkg, int y, void *line, int dtype); -SEP_API int sep_bkg_rmsline(const sep_bkg *bkg, int y, void *line, int dtype); +SEP_API int sep_bkg_line(const sep_bkg *bkg, int64_t y, void *line, int dtype); +SEP_API int sep_bkg_subline(const sep_bkg *bkg, int64_t y, void *line, int dtype); +SEP_API int sep_bkg_rmsline(const sep_bkg *bkg, int64_t y, void *line, int dtype); /* sep_bkg_[sub,rms]array() @@ -223,17 +223,17 @@ SEP_API void sep_bkg_free(sep_bkg *bkg); */ SEP_API int sep_extract(const sep_image *image, float thresh, /* detection threshold [1.5] */ - int thresh_type, /* threshold units [SEP_THRESH_REL] */ + int thresh_type, /* threshold units [SEP_THRESH_REL] */ int minarea, /* minimum area in pixels [5] */ const float *conv, /* convolution array (can be NULL) */ - /* [{1 2 1 2 4 2 1 2 1}] */ - int convw, int convh, /* w, h of convolution array [3,3] */ - int filter_type, /* convolution (0) or matched (1) [0] */ + /* [{1 2 1 2 4 2 1 2 1}] */ + int64_t convw, int64_t convh, /* w, h of convolution array [3,3] */ + int filter_type, /* convolution (0) or matched (1) [0] */ int deblend_nthresh, /* deblending thresholds [32] */ double deblend_cont, /* min. deblending contrast [0.005] */ int clean_flag, /* perform cleaning? [1] */ double clean_param, /* clean parameter [1.0] */ - sep_catalog **catalog); /* OUTPUT catalog */ + sep_catalog **catalog); /* OUTPUT catalog */ @@ -312,7 +312,7 @@ SEP_API int sep_sum_ellipann(const sep_image *image, * flag: Output flag (non-array). */ SEP_API int sep_sum_circann_multi(const sep_image *im, - double x, double y, double rmax, int n, int id, int subpix, + double x, double y, double rmax, int64_t n, int id, int subpix, short inflag, double *sum, double *sumvar, double *area, double *maskarea, short *flag); @@ -333,7 +333,7 @@ SEP_API int sep_sum_circann_multi(const sep_image *im, */ SEP_API int sep_flux_radius(const sep_image *im, double x, double y, double rmax, int id, int subpix, short inflag, - const double *fluxtot, const double *fluxfrac, int n, + const double *fluxtot, const double *fluxfrac, int64_t n, double *r, short *flag); /* sep_kron_radius() @@ -380,7 +380,7 @@ SEP_API int sep_windowed(const sep_image *im, * * Ellipse: cxx*(x'-x)^2 + cyy*(y'-y)^2 + cxy*(x'-x)*(y'-y) = r^2 */ -SEP_API void sep_set_ellipse(unsigned char *arr, int w, int h, +SEP_API void sep_set_ellipse(unsigned char *arr, int64_t w, int64_t h, double x, double y, double cxx, double cyy, double cxy, double r, unsigned char val); diff --git a/src/sepcore.h b/src/sepcore.h index f8a7396..5159266 100644 --- a/src/sepcore.h +++ b/src/sepcore.h @@ -47,8 +47,8 @@ typedef float PIXTYPE; /* type used inside of functions */ /* signature of converters */ typedef PIXTYPE (*converter)(const void *ptr); -typedef void (*array_converter)(const void *ptr, int n, PIXTYPE *target); -typedef void (*array_writer)(const float *ptr, int n, void *target); +typedef void (*array_converter)(const void *ptr, int64_t n, PIXTYPE *target); +typedef void (*array_writer)(const float *ptr, int64_t n, void *target); #define QCALLOC(ptr, typ, nel, status) \ {if (!(ptr = (typ *)calloc((size_t)(nel),sizeof(typ)))) \ @@ -76,13 +76,13 @@ typedef void (*array_writer)(const float *ptr, int n, void *target); }; \ } -float fqmedian(float *ra, int n); +float fqmedian(float *ra, int64_t n); void put_errdetail(const char *errtext); -int get_converter(int dtype, converter *f, int *size); -int get_array_converter(int dtype, array_converter *f, int *size); -int get_array_writer(int dtype, array_writer *f, int *size); -int get_array_subtractor(int dtype, array_writer *f, int *size); +int get_converter(int dtype, converter *f, int64_t *size); +int get_array_converter(int dtype, array_converter *f, int64_t *size); +int get_array_writer(int dtype, array_writer *f, int64_t *size); +int get_array_subtractor(int dtype, array_writer *f, int64_t *size); #if defined(_MSC_VER) #define _Thread_local __declspec(thread) diff --git a/src/util.c b/src/util.c index 9f49852..64718d1 100644 --- a/src/util.c +++ b/src/util.c @@ -24,7 +24,7 @@ #define DETAILSIZE 512 -const char *const sep_version_string = "1.2.0"; +const char *const sep_version_string = "1.3.0dev1"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ @@ -42,7 +42,7 @@ PIXTYPE convert_flt(const void *ptr) PIXTYPE convert_int(const void *ptr) { - return *(const int *)ptr; + return *(const int64_t *)ptr; } PIXTYPE convert_byt(const void *ptr) @@ -51,7 +51,7 @@ PIXTYPE convert_byt(const void *ptr) } /* return the correct converter depending on the datatype code */ -int get_converter(int dtype, converter *f, int *size) +int get_converter(int dtype, converter *f, int64_t *size) { int status = RETURN_OK; @@ -85,39 +85,39 @@ int get_converter(int dtype, converter *f, int *size) } /* array conversions */ -void convert_array_flt(const void *ptr, int n, PIXTYPE *target) +void convert_array_flt(const void *ptr, int64_t n, PIXTYPE *target) { const float *source = ptr; - int i; + int64_t i; for (i=0; if2? 1 : (f1 Date: Fri, 1 Dec 2023 16:57:29 +0100 Subject: [PATCH 06/47] Bug fix, update version and documentation. --- CHANGES.md | 5 ++--- README.md | 9 +++++++++ sep.pyx | 4 ++-- src/util.c | 12 ++++++------ 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 30f531b..7a6510b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -v1.3.0dev1 (27 November 2023) +v1.3.0 (1 December 2023) ==================== * The `segmentation_map` argument of `sep.extract()` will now accept either an @@ -11,8 +11,7 @@ v1.3.0dev1 (27 November 2023) the calculated thresholds (and any dependent parameters) may not be the same as originally derived. -* Use 64-bit integers throughout, to fix memory addressing with large arrays - (#122). +* Use 64-bit integers throughout, to fix memory addressing with large arrays ([#122](https://github.com/kbarbary/sep/issues/122 "Original issue"), inspired by [Gabe Brammer's fork](https://github.com/gbrammer/sep) with additional fixes). v1.2.1 (1 June 2022) diff --git a/README.md b/README.md index a0141e2..70afd70 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ +IMPORTANT +--------- + +This repository (all versions >= 1.3) was forked from https://github.com/kbarbary/sep in November 2023, as the original library no longer appears to be maintained. The current version can be installed by cloning this repository, and running +``` +python -m pip install . +``` +Changes are described in [CHANGES.md](./CHANGES.md), with all other documentation remaining unaltered. + SEP === diff --git a/sep.pyx b/sep.pyx index 4c2335f..7f34530 100644 --- a/sep.pyx +++ b/sep.pyx @@ -15,7 +15,7 @@ from cpython.version cimport PY_MAJOR_VERSION np.import_array() # To access the numpy C-API. -__version__ = "1.3.0dev1" +__version__ = "1.3.0" # ----------------------------------------------------------------------------- # Definitions from the SEP C library @@ -791,7 +791,7 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, ('ycpeak', np.int64), ('xpeak', np.int64), ('ypeak', np.int64), - ('flag', np.int64)])) + ('flag', np.int_)])) for i in range(catalog.nobj): result['thresh'][i] = catalog.thresh[i] diff --git a/src/util.c b/src/util.c index 64718d1..1988749 100644 --- a/src/util.c +++ b/src/util.c @@ -24,7 +24,7 @@ #define DETAILSIZE 512 -const char *const sep_version_string = "1.3.0dev1"; +const char *const sep_version_string = "1.3.0"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ @@ -42,7 +42,7 @@ PIXTYPE convert_flt(const void *ptr) PIXTYPE convert_int(const void *ptr) { - return *(const int64_t *)ptr; + return *(const int *)ptr; } PIXTYPE convert_byt(const void *ptr) @@ -164,10 +164,10 @@ void write_array_dbl(const float *ptr, int64_t n, void *target) void write_array_int(const float *ptr, int64_t n, void *target) { - int64_t *t = target; + int *t = target; int64_t i; for (i=0; i Date: Thu, 7 Dec 2023 15:08:53 +0100 Subject: [PATCH 07/47] PR#127, cmccully - Added back in a loop over objects in the deblending. --- sep.pyx | 2 +- src/deblend.c | 165 +++++++++++++++++++++++++------------------------- src/extract.c | 2 +- src/extract.h | 2 +- src/util.c | 2 +- 5 files changed, 86 insertions(+), 87 deletions(-) diff --git a/sep.pyx b/sep.pyx index 7f34530..5366c9f 100644 --- a/sep.pyx +++ b/sep.pyx @@ -15,7 +15,7 @@ from cpython.version cimport PY_MAJOR_VERSION np.import_array() # To access the numpy C-API. -__version__ = "1.3.0" +__version__ = "1.3.1dev" # ----------------------------------------------------------------------------- # Definitions from the SEP C library diff --git a/src/deblend.c b/src/deblend.c index 945f18b..3ad73c7 100644 --- a/src/deblend.c +++ b/src/deblend.c @@ -59,14 +59,14 @@ NOTE: Even if the object is not deblended, the output objlist threshold is This can return two error codes: DEBLEND_OVERFLOW or MEMORY_ALLOC_ERROR */ -int deblend(objliststruct *objlistin, int64_t l, objliststruct *objlistout, +int deblend(objliststruct *objlistin, objliststruct *objlistout, int deblend_nthresh, double deblend_mincont, int minarea, deblendctx *ctx) { objstruct *obj; objliststruct debobjlist, debobjlist2; double thresh, thresh0, value0; - int64_t h,i,j,k,m,subx,suby,subh,subw,xn, nbm = NBRANCH; + int64_t h,i,j,k,l,m,subx,suby,subh,subw,xn, nbm = NBRANCH; int64_t *submap; int status; @@ -88,6 +88,8 @@ int deblend(objliststruct *objlistin, int64_t l, objliststruct *objlistout, * The submap is used in lutz(). We create it here because we may call * lutz multiple times below, and we only want to create it once. */ + printf("L=%ld", l); + fflush(stdout); submap = createsubmap(objlistin, l, &subx, &suby, &subw, &subh); if (!submap) { @@ -95,98 +97,95 @@ int deblend(objliststruct *objlistin, int64_t l, objliststruct *objlistout, goto exit; } - /* set thresholds of object lists based on object threshold */ - thresh0 = objlistin->obj[l].thresh; - objlistout->thresh = debobjlist2.thresh = thresh0; + for (l=0; lnobj && status == RETURN_OK; l++) { - /* add input object to global deblending objlist and one local objlist */ - if ((status = addobjdeep(l, objlistin, &objlist[0])) != RETURN_OK) - goto exit; - if ((status = addobjdeep(l, objlistin, &debobjlist2)) != RETURN_OK) - goto exit; + /* set thresholds of object lists based on object threshold */ + thresh0 = objlistin->obj[l].thresh; + objlistout->thresh = debobjlist2.thresh = thresh0; - value0 = objlist[0].obj[0].fdflux*deblend_mincont; - ctx->ok[0] = (short)1; - for (k=1; kok[0] = (short)1; + for (k=1; kobj[l].fdpeak; debobjlist.thresh = thresh > 0.0? - thresh0*pow(thresh/thresh0,(double)k/xn) : thresh0; + thresh0*pow(thresh/thresh0,(double)k/xn) : thresh0; /*--------- Build tree (bottom->up) */ - if (objlist[k-1].nobj>=nsonmax) - { - status = DEBLEND_OVERFLOW; - goto exit; - } - - for (i=0; iplist, submap, subx, suby, subw, - &objlist[k-1].obj[i], &debobjlist, minarea, &ctx->lutz); - if (status != RETURN_OK) - goto exit; - - for (j=h=0; j=nsonmax) - { - status = DEBLEND_OVERFLOW; - goto exit; - } - if (h>=nbm-1) - if (!(ctx->son = (short *) - realloc(ctx->son,xn*nsonmax*(nbm+=16)*sizeof(short)))) - { - status = MEMORY_ALLOC_ERROR; - goto exit; - } - ctx->son[k-1+xn*(i+nsonmax*(h++))] = (short)m; - ctx->ok[k+xn*m] = (short)1; - } - ctx->son[k-1+xn*(i+nsonmax*h)] = (short)-1; - } + if (objlist[k-1].nobj>=nsonmax) { + status = DEBLEND_OVERFLOW; + goto exit; + } + + for (i=0; iplist, submap, subx, suby, subw, + &objlist[k-1].obj[i], &debobjlist, minarea, &ctx->lutz); + if (status != RETURN_OK) + goto exit; + + for (j=h=0; j=nsonmax) { + status = DEBLEND_OVERFLOW; + goto exit; + } + if (h>=nbm-1) { + if (!(ctx->son = (short *) + realloc(ctx->son,xn*nsonmax*(nbm+=16)*sizeof(short)))) { + status = MEMORY_ALLOC_ERROR; + goto exit; + } + } + ctx->son[k-1+xn*(i+nsonmax*(h++))] = (short)m; + ctx->ok[k+xn*m] = (short)1; + } + } + ctx->son[k-1+xn*(i+nsonmax*h)] = (short)-1; + } } - /*------- cut the right branches (top->down) */ - for (k = xn-2; k>=0; k--) - { + /*------- cut the right branches (top->down) */ + for (k = xn-2; k>=0; k--) { obj = objlist[k+1].obj; - for (i=0; ison[k+xn*(i+nsonmax*h)])!=-1; h++) - { - if (obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) - m++; - ctx->ok[k+xn*i] &= ctx->ok[k+1+xn*j]; - } - if (m>1) - { - for (h=0; (j=(int64_t)ctx->son[k+xn*(i+nsonmax*h)])!=-1; h++) - if (ctx->ok[k+1+xn*j] && - obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) - { - objlist[k+1].obj[j].flag |= SEP_OBJ_MERGED; - status = addobjdeep(j, &objlist[k+1], &debobjlist2); - if (status != RETURN_OK) - goto exit; - } - ctx->ok[k+xn*i] = (short)0; - } - } + for (i=0; ison[k+xn*(i+nsonmax*h)])!=-1; h++) { + if (obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) { + m++; + } + ctx->ok[k+xn*i] &= ctx->ok[k+1+xn*j]; + } + if (m>1) { + for (h=0; (j=(int64_t)ctx->son[k+xn*(i+nsonmax*h)])!=-1; h++) { + if (ctx->ok[k+1+xn*j] && + obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) { + objlist[k+1].obj[j].flag |= SEP_OBJ_MERGED; + status = addobjdeep(j, &objlist[k+1], &debobjlist2); + if (status != RETURN_OK) { + goto exit; + } + } + } + ctx->ok[k+xn*i] = (short)0; + } + } } - - if (ctx->ok[0]) - status = addobjdeep(0, &debobjlist2, objlistout); - else - status = gatherup(&debobjlist2, objlistout); + + if (ctx->ok[0]) + status = addobjdeep(0, &debobjlist2, objlistout); + else + status = gatherup(&debobjlist2, objlistout); + } exit: if (status == DEBLEND_OVERFLOW) diff --git a/src/extract.c b/src/extract.c index 40707a8..7792cd0 100644 --- a/src/extract.c +++ b/src/extract.c @@ -855,7 +855,7 @@ int sortit(infostruct *info, objliststruct *objlist, int minarea, preanalyse(0, objlist); - status = deblend(objlist, 0, &objlistout, deblend_nthresh, deblend_mincont, + status = deblend(objlist, &objlistout, deblend_nthresh, deblend_mincont, minarea, deblendctx); if (status) { diff --git a/src/extract.h b/src/extract.h index ec0d73e..d7a75dd 100644 --- a/src/extract.h +++ b/src/extract.h @@ -162,7 +162,7 @@ typedef struct { int allocdeblend(int deblend_nthresh, int64_t w, int64_t h, deblendctx *); void freedeblend(deblendctx *); -int deblend(objliststruct *, int64_t, objliststruct *, int, double, int, deblendctx *); +int deblend(objliststruct *, objliststruct *, int, double, int, deblendctx *); /*int addobjshallow(objstruct *, objliststruct *); int rmobjshallow(int, objliststruct *); diff --git a/src/util.c b/src/util.c index 1988749..970ac72 100644 --- a/src/util.c +++ b/src/util.c @@ -24,7 +24,7 @@ #define DETAILSIZE 512 -const char *const sep_version_string = "1.3.0"; +const char *const sep_version_string = "1.3.1dev"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ From dd4710ceb824e8bfb51d98c7bf5a7524c300a76c Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:41:48 +0100 Subject: [PATCH 08/47] PR#146, Bookaflok - Fix bug with improper cluster threshold set for variable threshold situations --- src/deblend.c | 2 -- src/extract.c | 26 ++++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/deblend.c b/src/deblend.c index 3ad73c7..1bb8332 100644 --- a/src/deblend.c +++ b/src/deblend.c @@ -88,8 +88,6 @@ int deblend(objliststruct *objlistin, objliststruct *objlistout, * The submap is used in lutz(). We create it here because we may call * lutz multiple times below, and we only want to create it once. */ - printf("L=%ld", l); - fflush(stdout); submap = createsubmap(objlistin, l, &subx, &suby, &subw, &subh); if (!submap) { diff --git a/src/extract.c b/src/extract.c index 7792cd0..098b251 100644 --- a/src/extract.c +++ b/src/extract.c @@ -60,6 +60,7 @@ int segsortit(infostruct *info, objliststruct *objlist, objliststruct *finalobjlist, double gain); void plistinit(int hasconv, int hasvar); void clean(objliststruct *objlist, double clean_param, int *survives); +PIXTYPE get_mean_thresh(infostruct *info, pliststruct *pixel); int convert_to_catalog(objliststruct *objlist, const int *survives, sep_catalog *cat, int64_t w, int include_pixels); @@ -676,7 +677,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (start[co] == UNKNOWN) { if ((int64_t)info[co].pixnb >= minarea) { /* update threshold before object is processed */ - objlist.thresh = thresh; + if (PLISTEXIST(thresh)) { + objlist.thresh = get_mean_thresh(&info[co], objlist.plist); + } else { + objlist.thresh = thresh; + } status = sortit(&info[co], &objlist, minarea, finalobjlist, deblend_nthresh, @@ -810,7 +815,7 @@ int segsortit(infostruct *info, objliststruct *objlist, obj.lastpix = info->lastpix; obj.flag = info->flag; - obj.thresh = PLISTPIX(objlist->plist+info->lastpix, thresh); + obj.thresh = get_mean_thresh(info, objlist->plist); analyse(0, objlist, 1, gain); @@ -1072,6 +1077,23 @@ void clean(objliststruct *objlist, double clean_param, int *survives) } /* outer loop of objlist (obj1) */ } +/************************** get_mean_thresh **********************************/ +/* Compute an average threshold from all pixels in the cluster */ + +PIXTYPE get_mean_thresh(infostruct *info, pliststruct *pixel) +{ + pliststruct *pixt; + int pix_accum=0; + PIXTYPE thresh_accum=0; + + for (pixt=pixel+info->firstpix; pixt>=pixel; pixt=pixel+PLIST(pixt,nextpix)) { + thresh_accum += PLISTPIX(pixt,thresh); + pix_accum++; + } + + return thresh_accum / pix_accum; +} + /*****************************************************************************/ /* sep_catalog manipulations */ From 43115c5878a91d5d105cce020ab68c07ae519df4 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:25:07 +0100 Subject: [PATCH 09/47] Move to dynamic version, remove deprecated setup.py features, add myself to authors list. --- .gitignore | 3 ++ AUTHORS.md | 2 ++ README.md | 2 +- pyproject.toml | 75 +++++++++++++++++++++++++++++++++++++++++++++++++- sep.pyx | 2 +- setup.py | 40 ++++----------------------- src/util.c | 2 +- 7 files changed, 88 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index bef801f..1c82178 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,6 @@ sep.c # tox .tox + +# version +*_version.py \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md index dc4769b..a80552c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -8,3 +8,5 @@ * Joe Lyman (@lyalpha): Make deblending limit settable * Michael Wuertenberger (@mworion): PyPI wheels * Ingvar Stepanyan (@rreverser): Build system, public API and thread safety fixes. +* Gabe Brammer (@gbrammer): Fix memory addressing with large arrays. +* Peter Watson (@PJ-Watson): Add extraction from existing segmentation map. \ No newline at end of file diff --git a/README.md b/README.md index 70afd70..485c0e0 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Building the development version (from github) requires Cython. Build and install in the usual place: ``` -./setup.py install +python -m pip install --editable . ``` **Run tests:** Tests require the [pytest](http://pytest.org) Python diff --git a/pyproject.toml b/pyproject.toml index b0af0b5..1d199bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,76 @@ [build-system] -requires = ["setuptools", "wheel", "oldest-supported-numpy", "Cython"] +requires = ["setuptools>=61.0", "wheel", "oldest-supported-numpy", "Cython", "setuptools_scm>=8.0"] build-backend = "setuptools.build_meta" + +[project] +name = "sep" +dynamic = ["version"] +authors = [ + { name="Emmanuel Bertin"}, + { name="Kyle Barbary"}, + { name="Kyle Boone"}, + { name="Thomas Robitaille"}, + { name="Matt Craig"}, + { name="Curtis McCully"}, + { name="Evert Rol"}, + { name="Joe Lyman"}, + { name="Michael Wuertenberger"}, + { name="Ingvar Stepanyan"}, + { name="Gabe Brammer"}, + { name="Peter Watson", email="peter.watson+pyGFE@inaf.it" }, +] +maintainers = [ + { name="Peter Watson", email="peter.watson+pyGFE@inaf.it" }, +] +description="Astronomical source extraction and photometry library" +readme = "README.md" +license = { text = "LGPLv3+" } +classifiers=[ + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", + "Topic :: Scientific/Engineering :: Astronomy", + "Intended Audience :: Science/Research", +] +requires-python = ">=3.9" +dependencies = [ + "numpy>=1.23" +] + +[project.urls] +"Homepage" = "https://github.com/PJ-Watson/sep" +"Bug Tracker" = "https://github.com/PJ-Watson/sep/issues" +"(Old) Documentation" = "http://sep.readthedocs.org" + +[tool.setuptools_scm] +version_file = "src/_version.py" + +# [tool.black] +# line-length = 88 +# target-version = ['py311'] +# extend-exclude = '(.*.txt|.*.md|.*.toml|.*.odg)' +# preview = true + +# [tool.isort] +# profile = "black" + +# [tool.numpydoc_validation] +# checks = [ +# "all", # report on all checks, except the below +# "EX01", +# "SA01", +# "ES01", +# ] +# exclude = [ # don't report on objects that match any of these regex +# '\.undocumented_method$', +# '\.__repr__$', +# '\._.*$', # Ignore anything that's private (e.g., starts with _) +# 'seg_map_viewer*', +# 'qt_utils*', +# 'GUI_main*', +# ] +# override_SS05 = [ # override SS05 to allow docstrings starting with these words +# '^Process ', +# '^Assess ', +# '^Access ', +# ] \ No newline at end of file diff --git a/sep.pyx b/sep.pyx index 5366c9f..0523488 100644 --- a/sep.pyx +++ b/sep.pyx @@ -15,7 +15,7 @@ from cpython.version cimport PY_MAJOR_VERSION np.import_array() # To access the numpy C-API. -__version__ = "1.3.1dev" +from _version import version as __version__ # ----------------------------------------------------------------------------- # Definitions from the SEP C library diff --git a/setup.py b/setup.py index 2a61ebd..31c0a47 100755 --- a/setup.py +++ b/setup.py @@ -1,17 +1,11 @@ -#!/usr/bin/env python - import os import re import sys from glob import glob -from setuptools import setup +# from setuptools import setup from setuptools.dist import Distribution -from setuptools.extension import Extension - -# Synchronize version from code. -fname = "sep.pyx" -version = re.findall(r"__version__ = \"(.*?)\"", open(fname).read())[0] +from setuptools import Extension, setup # Detect if setup.py is being run with an argument that doesn't require # building anything. (egg info, help commands, etc) @@ -30,35 +24,13 @@ else: import numpy from Cython.Build import cythonize - sourcefiles = [fname] + glob(os.path.join("src", "*.c")) + sourcefiles = ["sep.pyx"] + glob(os.path.join("src", "*.c")) headerfiles = glob(os.path.join("src", "*.h")) include_dirs = [numpy.get_include(), "src"] extensions = [Extension("sep", sourcefiles, include_dirs=include_dirs, depends=headerfiles, define_macros=[("_USE_MATH_DEFINES", "1")])] extensions = cythonize(extensions) - - -description = "Astronomical source extraction and photometry library" -long_description = "http://sep.readthedocs.org" - -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Programming Language :: Python :: 3", - "License :: OSI Approved :: GNU Lesser General Public License v3 " - "or later (LGPLv3+)", - "Topic :: Scientific/Engineering :: Astronomy", - "Intended Audience :: Science/Research"] - -setup(name="sep", - version=version, - description=description, - long_description=long_description, - license="LGPLv3+", - classifiers=classifiers, - url="https://github.com/kbarbary/sep", - author="Kyle Barbary", - author_email="kylebarbary@gmail.com", - python_requires='>=3.5', - install_requires=['numpy'], - ext_modules=extensions) +setup( + ext_modules=extensions +) diff --git a/src/util.c b/src/util.c index 970ac72..ff3f72d 100644 --- a/src/util.c +++ b/src/util.c @@ -24,7 +24,7 @@ #define DETAILSIZE 512 -const char *const sep_version_string = "1.3.1dev"; +const char *const sep_version_string = "1.3.1.dev"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ From 61f3f97e17db02528f207fe90f67e9bc7f75fcb8 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:38:17 +0100 Subject: [PATCH 10/47] Fix benchmarks - python2 xrange and deprecated imports. --- bench.py | 103 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/bench.py b/bench.py index 6084cfb..acc285d 100755 --- a/bench.py +++ b/bench.py @@ -1,14 +1,17 @@ #!/usr/bin/env python from __future__ import print_function -from os.path import join import time +from os.path import join + import numpy as np + import sep # try to import photutils for comparison timing try: import photutils + HAVE_PHOTUTILS = True except ImportError: HAVE_PHOTUTILS = False @@ -16,11 +19,13 @@ # Try to import any FITS reader try: from fitsio import read as getdata + HAVE_FITS = True NEED_BYTESWAP = False except ImportError: try: from astropy.io.fits import getdata + HAVE_FITS = True except ImportError: HAVE_FITS = False @@ -37,30 +42,31 @@ t0 = time.time() bkg = sep.Background(data) # estimate background t1 = time.time() - print("measure background: {0:6.2f} ms".format((t1-t0) * 1.e3)) + print("measure background: {0:6.2f} ms".format((t1 - t0) * 1.0e3)) t0 = time.time() bkg.subfrom(data) # subtract it t1 = time.time() - print("subtract background: {0:6.2f} ms".format((t1-t0) * 1.e3)) + print("subtract background: {0:6.2f} ms".format((t1 - t0) * 1.0e3)) t0 = time.time() backarr = bkg.back(dtype=np.float64) # background t1 = time.time() - print("background array: {0:6.2f} ms".format((t1-t0) * 1.e3)) + print("background array: {0:6.2f} ms".format((t1 - t0) * 1.0e3)) t0 = time.time() rmsarr = bkg.rms() t1 = time.time() - print("rms array: {0:6.2f} ms".format((t1-t0) * 1.e3)) + print("rms array: {0:6.2f} ms".format((t1 - t0) * 1.0e3)) t0 = time.time() objects = sep.extract(data, 1.5 * bkg.globalrms) t1 = time.time() - print("extract: {0:6.2f} ms [{1:d} objects]" - .format((t1-t0) * 1.e3, len(objects))) + print( + "extract: {0:6.2f} ms [{1:d} objects]".format((t1 - t0) * 1.0e3, len(objects)) + ) -#-------------------------------------------------------------------------- +# -------------------------------------------------------------------------- # Background subtraction print("") @@ -70,8 +76,9 @@ print(""" | test | sep | photutils | ratio | |-------------------------|-----------------|-----------------|--------|""") - blankline = \ -"| | | | |" + blankline = ( + "| | | | |" + ) else: print("sep version: ", sep.__version__) @@ -87,26 +94,26 @@ line = "| {0:4d}^2 image background |".format(data.shape[0]) t0 = time.time() - for _ in xrange(0, nloop): + for _ in range(0, nloop): bkg = sep.Background(data) t1 = time.time() - t_sep = (t1-t0) * 1.e3 / nloop + t_sep = (t1 - t0) * 1.0e3 / nloop line += " {0:7.2f} ms |".format(t_sep) if HAVE_PHOTUTILS: t0 = time.time() - for _ in xrange(0, nloop): + for _ in range(0, nloop): try: - bkg = photutils.Background(data, (64, 64)) # estimate background + bkg = photutils.background.Background(data, (64, 64)) # estimate background except AttributeError: - bkg = photutils.Background2D(data, (64, 64)) # estimate background + bkg = photutils.background.Background2D(data, (64, 64)) # estimate background t1 = time.time() - t_pu = (t1-t0) * 1.e3 / nloop - line += " {0:7.2f} ms | {1:6.2f} |".format(t_pu, t_pu/t_sep) + t_pu = (t1 - t0) * 1.0e3 / nloop + line += " {0:7.2f} ms | {1:6.2f} |".format(t_pu, t_pu / t_sep) print(line) -#------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ # Circular aperture photometry benchmarks if not CONDENSED: @@ -118,16 +125,19 @@ naper = 1000 data = np.ones((2000, 2000), dtype=np.float32) -x = np.random.uniform(200., 1800., naper) -y = np.random.uniform(200., 1800., naper) +x = np.random.uniform(200.0, 1800.0, naper) +y = np.random.uniform(200.0, 1800.0, naper) if CONDENSED: - r_list = [5.] + r_list = [5.0] subpix_list = [(5, "subpixel", "subpix=5"), (0, "exact", "exact")] else: - r_list = [3., 5., 10., 20.] - subpix_list = [(1, "center", "subpix=1"), (5, "subpixel", "subpix=5"), - (0, "exact", "exact")] + r_list = [3.0, 5.0, 10.0, 20.0] + subpix_list = [ + (1, "center", "subpix=1"), + (5, "subpixel", "subpix=5"), + (0, "exact", "exact"), + ] for r in r_list: for subpix, method, label in subpix_list: @@ -135,51 +145,54 @@ line = "| circles r={0:2d} {1:8s} |".format(int(r), label) t0 = time.time() - for _ in xrange(0, nloop): + for _ in range(0, nloop): flux, fluxerr, flag = sep.sum_circle(data, x, y, r, subpix=subpix) t1 = time.time() - t_sep = (t1-t0) * 1.e6 / naper / nloop + t_sep = (t1 - t0) * 1.0e6 / naper / nloop line += " {0:7.2f} us/aper |".format(t_sep) if HAVE_PHOTUTILS: - apertures = photutils.CircularAperture((x, y), r) + apertures = photutils.aperture.CircularAperture(np.column_stack((x,y)), r) t0 = time.time() - for _ in xrange(0, nloop): - res = photutils.aperture_photometry( - data, apertures, method=method, subpixels=subpix) + for _ in range(0, nloop): + res = photutils.aperture.aperture_photometry( + data, apertures, method=method, subpixels=subpix + ) t1 = time.time() - t_pu = (t1-t0) * 1.e6 / naper / nloop - line += " {0:7.2f} us/aper | {1:6.2f} |".format(t_pu, t_pu/t_sep) + t_pu = (t1 - t0) * 1.0e6 / naper / nloop + line += " {0:7.2f} us/aper | {1:6.2f} |".format(t_pu, t_pu / t_sep) print(line) if not CONDENSED: print(blankline) -a = 1. -b = 1. -theta = np.pi/4. +a = 1.0 +b = 1.0 +theta = np.pi / 4.0 for r in r_list: for subpix, method, label in subpix_list: line = "| ellipses r={0:2d} {1:8s} |".format(int(r), label) t0 = time.time() - for _ in xrange(0, nloop): - flux, fluxerr, flag = sep.sum_ellipse(data, x, y, a, b, theta, r, - subpix=subpix) + for _ in range(0, nloop): + flux, fluxerr, flag = sep.sum_ellipse( + data, x, y, a, b, theta, r, subpix=subpix + ) t1 = time.time() - t_sep = (t1-t0) * 1.e6 / naper / nloop + t_sep = (t1 - t0) * 1.0e6 / naper / nloop line += " {0:7.2f} us/aper |".format(t_sep) if HAVE_PHOTUTILS: - apertures = photutils.EllipticalAperture((x, y), a*r, b*r, theta) + apertures = photutils.aperture.EllipticalAperture(np.column_stack((x,y)), a * r, b * r, theta) t0 = time.time() - for _ in xrange(0, nloop): - res = photutils.aperture_photometry( - data, apertures, method=method, subpixels=subpix) + for _ in range(0, nloop): + res = photutils.aperture.aperture_photometry( + data, apertures, method=method, subpixels=subpix + ) t1 = time.time() - t_pu = (t1-t0) * 1.e6 / naper / nloop - line += " {0:7.2f} us/aper | {1:6.2f} |".format(t_pu, t_pu/t_sep) + t_pu = (t1 - t0) * 1.0e6 / naper / nloop + line += " {0:7.2f} us/aper | {1:6.2f} |".format(t_pu, t_pu / t_sep) print(line) From 96b315e4df1bbed6f133a739d5927c9f308e63ef Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:46:45 +0100 Subject: [PATCH 11/47] Various formatting fixes. --- AUTHORS.md | 2 +- CHANGES.md | 14 +++++++------- README.md | 6 +++--- bench.py | 14 ++++++++++---- data/README.md | 2 +- docs/apertures.rst | 12 ++++++------ docs/filter.rst | 4 ++-- docs/reference.rst | 4 ++-- 8 files changed, 32 insertions(+), 26 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index a80552c..7063849 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -9,4 +9,4 @@ * Michael Wuertenberger (@mworion): PyPI wheels * Ingvar Stepanyan (@rreverser): Build system, public API and thread safety fixes. * Gabe Brammer (@gbrammer): Fix memory addressing with large arrays. -* Peter Watson (@PJ-Watson): Add extraction from existing segmentation map. \ No newline at end of file +* Peter Watson (@PJ-Watson): Add extraction from existing segmentation map. diff --git a/CHANGES.md b/CHANGES.md index 7a6510b..aade567 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,14 +1,14 @@ v1.3.0 (1 December 2023) ==================== -* The `segmentation_map` argument of `sep.extract()` will now accept either an - array or boolean. If an existing segmentation map is passed, the object - detection stage is skipped, and sources will be individually analysed - according to the provided map. This change is backwards-compatible with +* The `segmentation_map` argument of `sep.extract()` will now accept either an + array or boolean. If an existing segmentation map is passed, the object + detection stage is skipped, and sources will be individually analysed + according to the provided map. This change is backwards-compatible with respect to the Python module. - - Please note that as no deblending is performed, - the calculated thresholds (and any dependent parameters) may not be the same + + Please note that as no deblending is performed, + the calculated thresholds (and any dependent parameters) may not be the same as originally derived. * Use 64-bit integers throughout, to fix memory addressing with large arrays ([#122](https://github.com/kbarbary/sep/issues/122 "Original issue"), inspired by [Gabe Brammer's fork](https://github.com/gbrammer/sep) with additional fixes). diff --git a/README.md b/README.md index 485c0e0..aae5fa8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ IMPORTANT --------- -This repository (all versions >= 1.3) was forked from https://github.com/kbarbary/sep in November 2023, as the original library no longer appears to be maintained. The current version can be installed by cloning this repository, and running +This repository (all versions >= 1.3) was forked from https://github.com/kbarbary/sep in November 2023, as the original library no longer appears to be maintained. The current version can be installed by cloning this repository, and running ``` python -m pip install . ``` @@ -16,7 +16,7 @@ Python and C library for Source Extraction and Photometry [![PyPI](https://img.shields.io/pypi/v/sep.svg)](https://pypi.python.org/pypi/sep) [![JOSS](http://joss.theoj.org/papers/10.21105/joss.00058/status.svg)](http://dx.doi.org/10.21105/joss.00058) -*"... [it's] an SEP: Somebody Else's Problem." +*"... [it's] an SEP: Somebody Else's Problem." "Oh, good. I can relax then."* @@ -188,7 +188,7 @@ executable built on top of the library. In Source Extractor, background estimation, object detection and photometry are deeply integrated into the Source Extractor executable. Many changes to the code were necessary in order to put the functionality in stand-alone C functions. It's too much -to ask of the Source Extractor developer to rewrite large parts of the +to ask of the Source Extractor developer to rewrite large parts of the core of the Source Extractor program with little gain for the executable. **What sort of changes?** diff --git a/bench.py b/bench.py index acc285d..80e0993 100755 --- a/bench.py +++ b/bench.py @@ -104,9 +104,13 @@ t0 = time.time() for _ in range(0, nloop): try: - bkg = photutils.background.Background(data, (64, 64)) # estimate background + bkg = photutils.background.Background( + data, (64, 64) + ) # estimate background except AttributeError: - bkg = photutils.background.Background2D(data, (64, 64)) # estimate background + bkg = photutils.background.Background2D( + data, (64, 64) + ) # estimate background t1 = time.time() t_pu = (t1 - t0) * 1.0e3 / nloop line += " {0:7.2f} ms | {1:6.2f} |".format(t_pu, t_pu / t_sep) @@ -152,7 +156,7 @@ line += " {0:7.2f} us/aper |".format(t_sep) if HAVE_PHOTUTILS: - apertures = photutils.aperture.CircularAperture(np.column_stack((x,y)), r) + apertures = photutils.aperture.CircularAperture(np.column_stack((x, y)), r) t0 = time.time() for _ in range(0, nloop): res = photutils.aperture.aperture_photometry( @@ -185,7 +189,9 @@ line += " {0:7.2f} us/aper |".format(t_sep) if HAVE_PHOTUTILS: - apertures = photutils.aperture.EllipticalAperture(np.column_stack((x,y)), a * r, b * r, theta) + apertures = photutils.aperture.EllipticalAperture( + np.column_stack((x, y)), a * r, b * r, theta + ) t0 = time.time() for _ in range(0, nloop): res = photutils.aperture.aperture_photometry( diff --git a/data/README.md b/data/README.md index 9ea1124..ef316a8 100644 --- a/data/README.md +++ b/data/README.md @@ -7,4 +7,4 @@ The comparison catalog file `image.cat` is generated by running sextractor image.fits -CATALOG_NAME image.cat ``` -in this directory. \ No newline at end of file +in this directory. diff --git a/docs/apertures.rst b/docs/apertures.rst index b802b50..03c1d67 100644 --- a/docs/apertures.rst +++ b/docs/apertures.rst @@ -38,7 +38,7 @@ that the Poisson uncertainty on the total sum should be included. Some illustrative examples:: # Specify a per-pixel "background" error and a gain. This is suitable - # when the data have been background subtracted. + # when the data have been background subtracted. flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 3.0, err=bkg.globalrms, gain=1.0) @@ -73,7 +73,7 @@ noise in each pixel, :math:`F` is the sum in the aperture and :math:`g` is the gain. The last term is not added if ``gain`` is `None`. -**Masking** +**Masking** Apply a mask (same shape as data). Pixels where the mask is True are "corrected" to the average value within the aperture. :: @@ -164,20 +164,20 @@ of 0.5 and a normalizing flux of ``FLUX_AUTO``. The equivalent here is:: Segmentation-masked image measurements -------------------------------------- -SourceExtractor provides a mechanism for measuring the "AUTO" and "FLUX_RADIUS" parameters for a given object including a mask for neighboring sources. In addition to the mask, setting the SourceExtractor parameter ``MASK_TYPE=CORRECT`` further fills the masked pixels of a given source with "good" pixel values reflected opposite of the masked pixels. The ``SEP`` photometry and measurement functions provide an option for simple masking without reflection or subtracting neighbor flux. +SourceExtractor provides a mechanism for measuring the "AUTO" and "FLUX_RADIUS" parameters for a given object including a mask for neighboring sources. In addition to the mask, setting the SourceExtractor parameter ``MASK_TYPE=CORRECT`` further fills the masked pixels of a given source with "good" pixel values reflected opposite of the masked pixels. The ``SEP`` photometry and measurement functions provide an option for simple masking without reflection or subtracting neighbor flux. For example, using a segmentation array provided by ``sep.extract``, we can compute the masked ``flux_radius`` that could otherwise be artificially large due to flux from nearby sources:: # list of object id numbers that correspond to the segments seg_id = np.arange(1, len(objs)+1, dtype=np.int32) - + r, flag = sep.flux_radius(data, objs['x'], objs['y'], 6.*objs['a'], 0.5, - seg_id=seg_id, seg=seg, + seg_id=seg_id, seg=seg, normflux=flux, subpix=5) To enforce that a given measurement **only** includes pixels within a segment, provide negative values in the ``seg_id`` list. Otherwise the mask for a given object will be pixels with ``(seg == 0) | (seg_id == id_i)``. -The following functions include the segmentation masking: ``sum_circle``, ``sum_circann``, ``sum_ellipse``, ``sum_ellipann``, ``flux_radius`` , and ``kron_radius`` (``winpos`` **currently does not**). +The following functions include the segmentation masking: ``sum_circle``, ``sum_circann``, ``sum_ellipse``, ``sum_ellipann``, ``flux_radius`` , and ``kron_radius`` (``winpos`` **currently does not**). Masking image regions --------------------- diff --git a/docs/filter.rst b/docs/filter.rst index 61ff6e6..e8fece9 100644 --- a/docs/filter.rst +++ b/docs/filter.rst @@ -14,7 +14,7 @@ shape. This is controlled using the ``filter_kernel`` keyword in objects = sep.extract(data, thresh, filter_kernel=kernel) If ``filter_kernel`` is not specified, a default 3-by-3 kernel -is used. To disable filtering entirely, specify ``filter_kernel=None``. +is used. To disable filtering entirely, specify ``filter_kernel=None``. What array should be used for ``filter_kernel``? It should be approximately the shape of the objects you are trying to detect. For @@ -129,7 +129,7 @@ standard deviations above the background. This gives: .. math:: &E[(T^T N)^2] = T^T C T = \alpha^2 S^T C^{-1} C C^{-1} S = \alpha^2 S^T - C^{-1} S = 1 \\ + C^{-1} S = 1 \\ &\alpha = \frac{1}{\sqrt{S^T C^{-1} S}} Putting everything together, our normalized linear transformation is: diff --git a/docs/reference.rst b/docs/reference.rst index 39f9f9d..07a114b 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -6,7 +6,7 @@ Reference/API .. autosummary:: :toctree: api - + sep.Background sep.extract @@ -14,7 +14,7 @@ Reference/API .. autosummary:: :toctree: api - + sep.sum_circle sep.sum_circann sep.sum_ellipse From 1cc94098663184913d7099fff496c971ceb7a4ed Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 31 Jan 2024 17:51:52 +0100 Subject: [PATCH 12/47] Follow black formatting. --- .../workflows/build-wheels-upload-pypi.yml | 2 +- .gitignore | 4 +- .pre-commit-config.yaml | 22 +++ FindSEP.cmake | 2 +- ctest/compare.py | 16 +- ctest/test_image.c | 2 - docs/conf.py | 165 +++++++++--------- docs/rtd-pip-requirements | 2 +- pyproject.toml | 51 +++--- sep.pyx | 7 +- setup.py | 25 ++- src/convolve.c | 2 +- src/deblend.c | 2 +- src/extract.c | 18 +- src/overlap.h | 4 +- tox.ini | 2 +- 16 files changed, 178 insertions(+), 148 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/build-wheels-upload-pypi.yml b/.github/workflows/build-wheels-upload-pypi.yml index 7b9b0f7..5b3d2ef 100644 --- a/.github/workflows/build-wheels-upload-pypi.yml +++ b/.github/workflows/build-wheels-upload-pypi.yml @@ -69,7 +69,7 @@ jobs: runs-on: ${{ matrix.os }} env: CIBW_ARCHS_MACOS: "x86_64 universal2 arm64" - + strategy: max-parallel: 4 matrix: diff --git a/.gitignore b/.gitignore index 1c82178..c4f278c 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,7 @@ sep.c # PyCharm .idea -.project +.project # Eclipse .pydevproject @@ -56,4 +56,4 @@ sep.c .tox # version -*_version.py \ No newline at end of file +*_version.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a652d65 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +exclude: "data/*" +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files +- repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort (python) +- repo: https://github.com/psf/black + rev: 23.12.1 + hooks: + - id: black +# - repo: https://github.com/numpy/numpydoc +# rev: v1.6.0 +# hooks: +# - id: numpydoc-validation diff --git a/FindSEP.cmake b/FindSEP.cmake index 1f32ad4..54aa3b3 100644 --- a/FindSEP.cmake +++ b/FindSEP.cmake @@ -48,5 +48,5 @@ else (SEP_LIBRARIES) endif (SEP_FOUND) mark_as_advanced(SEP_LIBRARIES) - + endif (SEP_LIBRARIES) diff --git a/ctest/compare.py b/ctest/compare.py index aded581..770705e 100755 --- a/ctest/compare.py +++ b/ctest/compare.py @@ -4,11 +4,13 @@ import sys + def read_table(fname): rows = [] - for line in open(fname, 'r'): + for line in open(fname, "r"): l = line.strip() - if len(l) == 0 or l[0] == '#': continue + if len(l) == 0 or l[0] == "#": + continue items = l.split() typed_items = [] for it in items: @@ -19,12 +21,12 @@ def read_table(fname): rows.append(typed_items) return rows + def fracdiff(x, y): return abs((y - x) / max(y, x)) - -if __name__ == '__main__': +if __name__ == "__main__": fname1, fname2 = sys.argv[1:3] @@ -38,8 +40,8 @@ def fracdiff(x, y): assert len(ref) == len(sep) for i in range(len(ref)): - assert abs(ref[i][1] - sep[i][1] - 1.) < 1.e-3 # x coordinate - assert abs(ref[i][2] - sep[i][2] - 1.) < 1.e-3 # y coordinate - assert fracdiff(ref[i][12], sep[i][3]) < 2.e-4 # flux + assert abs(ref[i][1] - sep[i][1] - 1.0) < 1.0e-3 # x coordinate + assert abs(ref[i][2] - sep[i][2] - 1.0) < 1.0e-3 # y coordinate + assert fracdiff(ref[i][12], sep[i][3]) < 2.0e-4 # flux print("compare passed") diff --git a/ctest/test_image.c b/ctest/test_image.c index 0bd5d95..1245821 100644 --- a/ctest/test_image.c +++ b/ctest/test_image.c @@ -343,5 +343,3 @@ int main(int argc, char **argv) free(ycs); } */ - - diff --git a/docs/conf.py b/docs/conf.py index e9aa230..aa98fef 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,41 +12,44 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys import os +import sys + import sphinx_rtd_theme + import sep # generate api directory if it doesn't already exist -if not os.path.exists('api'): - os.mkdir('api') +if not os.path.exists("api"): + os.mkdir("api") # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) -#sys.path.append(os.path.abspath('ext')) +# sys.path.insert(0, os.path.abspath('.')) +# sys.path.append(os.path.abspath('ext')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' intersphinx_mapping = { - 'python': ('http://docs.python.org/', None), - 'numpy': ('http://docs.scipy.org/doc/numpy/', None)} + "python": ("http://docs.python.org/", None), + "numpy": ("http://docs.scipy.org/doc/numpy/", None), +} # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'numpydoc', - 'nbsphinx', - 'IPython.sphinxext.ipython_console_highlighting' + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.intersphinx", + "sphinx.ext.mathjax", + "numpydoc", + "nbsphinx", + "IPython.sphinxext.ipython_console_highlighting", ] numpydoc_show_class_members = False autosummary_generate = ["reference.rst"] @@ -54,68 +57,68 @@ autodoc_default_flags = ["members", "no-special-members"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'sep' -copyright = u'2014-2016, Kyle Barbary and contributors' +project = "sep" +copyright = "2014-2016, Kyle Barbary and contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # THe short X.Y version. -version = '.'.join(sep.__version__.split('.')[0:2]) +version = ".".join(sep.__version__.split(".")[0:2]) # The full version, including alpha/beta/rc tags. release = sep.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', '**.ipynb_checkpoints'] +exclude_patterns = ["_build", "**.ipynb_checkpoints"] # The reST default role (used for this markup: `text`) to use for all # documents. -default_role = 'obj' +default_role = "obj" # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # -- Options for HTML output ---------------------------------------------- @@ -128,26 +131,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -157,106 +160,100 @@ # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'sepdoc' +htmlhelp_basename = "sepdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'sep.tex', u'sep Documentation', - u'Kyle Barbary', 'manual'), + ("index", "sep.tex", "sep Documentation", "Kyle Barbary", "manual"), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'sep', u'sep Documentation', - [u'Kyle Barbary'], 1) -] +man_pages = [("index", "sep", "sep Documentation", ["Kyle Barbary"], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -265,19 +262,25 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'sep', u'sep Documentation', - u'Kyle Barbary', 'sep', 'One line description of project.', - 'Miscellaneous'), + ( + "index", + "sep", + "sep Documentation", + "Kyle Barbary", + "sep", + "One line description of project.", + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/docs/rtd-pip-requirements b/docs/rtd-pip-requirements index a21210b..1f0edad 100644 --- a/docs/rtd-pip-requirements +++ b/docs/rtd-pip-requirements @@ -4,4 +4,4 @@ nbsphinx matplotlib fitsio numpydoc -jupyter \ No newline at end of file +jupyter diff --git a/pyproject.toml b/pyproject.toml index 1d199bf..51b2cf9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,32 +45,29 @@ dependencies = [ [tool.setuptools_scm] version_file = "src/_version.py" -# [tool.black] -# line-length = 88 -# target-version = ['py311'] -# extend-exclude = '(.*.txt|.*.md|.*.toml|.*.odg)' -# preview = true +[tool.black] +line-length = 88 +target-version = ['py311'] +extend-exclude = '(.*.txt|.*.md|.*.toml|.*.odg)' +preview = true -# [tool.isort] -# profile = "black" +[tool.isort] +profile = "black" -# [tool.numpydoc_validation] -# checks = [ -# "all", # report on all checks, except the below -# "EX01", -# "SA01", -# "ES01", -# ] -# exclude = [ # don't report on objects that match any of these regex -# '\.undocumented_method$', -# '\.__repr__$', -# '\._.*$', # Ignore anything that's private (e.g., starts with _) -# 'seg_map_viewer*', -# 'qt_utils*', -# 'GUI_main*', -# ] -# override_SS05 = [ # override SS05 to allow docstrings starting with these words -# '^Process ', -# '^Assess ', -# '^Access ', -# ] \ No newline at end of file +[tool.numpydoc_validation] +checks = [ + "all", # report on all checks, except the below + "EX01", + "SA01", + "ES01", +] +exclude = [ # don't report on objects that match any of these regex + '\.undocumented_method$', + '\.__repr__$', + '\._.*$', # Ignore anything that's private (e.g., starts with _) +] +override_SS05 = [ # override SS05 to allow docstrings starting with these words + '^Process ', + '^Assess ', + '^Access ', +] diff --git a/sep.pyx b/sep.pyx index 0523488..ab71a4b 100644 --- a/sep.pyx +++ b/sep.pyx @@ -6,12 +6,13 @@ Source Extraction and Photometry This module is a wrapper of the SEP C library. """ import numpy as np + +cimport cython cimport numpy as np +from cpython.mem cimport PyMem_Free, PyMem_Malloc +from cpython.version cimport PY_MAJOR_VERSION from libc cimport limits from libc.math cimport sqrt -cimport cython -from cpython.mem cimport PyMem_Malloc, PyMem_Free -from cpython.version cimport PY_MAJOR_VERSION np.import_array() # To access the numpy C-API. diff --git a/setup.py b/setup.py index 31c0a47..267d870 100755 --- a/setup.py +++ b/setup.py @@ -3,17 +3,18 @@ import sys from glob import glob +from setuptools import Extension, setup + # from setuptools import setup from setuptools.dist import Distribution -from setuptools import Extension, setup # Detect if setup.py is being run with an argument that doesn't require # building anything. (egg info, help commands, etc) -options = Distribution.display_option_names + ['help-commands', 'help'] +options = Distribution.display_option_names + ["help-commands", "help"] is_non_build = ( - any('--' + opt in sys.argv for opt in options) + any("--" + opt in sys.argv for opt in options) or len(sys.argv) == 1 - or sys.argv[1] in ('egg_info', 'clean', 'help') + or sys.argv[1] in ("egg_info", "clean", "help") ) # extension module(s): only add if we actually need to build, because we need @@ -24,13 +25,19 @@ else: import numpy from Cython.Build import cythonize + sourcefiles = ["sep.pyx"] + glob(os.path.join("src", "*.c")) headerfiles = glob(os.path.join("src", "*.h")) include_dirs = [numpy.get_include(), "src"] - extensions = [Extension("sep", sourcefiles, include_dirs=include_dirs, - depends=headerfiles, define_macros=[("_USE_MATH_DEFINES", "1")])] + extensions = [ + Extension( + "sep", + sourcefiles, + include_dirs=include_dirs, + depends=headerfiles, + define_macros=[("_USE_MATH_DEFINES", "1")], + ) + ] extensions = cythonize(extensions) -setup( - ext_modules=extensions -) +setup(ext_modules=extensions) diff --git a/src/convolve.c b/src/convolve.c index fb27998..8f4701e 100644 --- a/src/convolve.c +++ b/src/convolve.c @@ -36,7 +36,7 @@ f* GNU Lesser General Public License for more details. * convw, convh : width and height of conv * buf : output convolved line (buf->dw elements long) */ -int convolve(arraybuffer *buf, int64_t y, const float *conv, +int convolve(arraybuffer *buf, int64_t y, const float *conv, int64_t convw, int64_t convh, PIXTYPE *out) { int64_t convw2, convn, cx, cy, i, dcx, y0; diff --git a/src/deblend.c b/src/deblend.c index 1bb8332..279e722 100644 --- a/src/deblend.c +++ b/src/deblend.c @@ -178,7 +178,7 @@ int deblend(objliststruct *objlistin, objliststruct *objlistout, } } } - + if (ctx->ok[0]) status = addobjdeep(0, &debobjlist2, objlistout); else diff --git a/src/extract.c b/src/extract.c index 098b251..545dffa 100644 --- a/src/extract.c +++ b/src/extract.c @@ -56,7 +56,7 @@ size_t sep_get_extract_pixstack() int sortit(infostruct *info, objliststruct *objlist, int minarea, objliststruct *finalobjlist, int deblend_nthresh, double deblend_mincont, double gain, deblendctx *deblendctx); -int segsortit(infostruct *info, objliststruct *objlist, +int segsortit(infostruct *info, objliststruct *objlist, objliststruct *finalobjlist, double gain); void plistinit(int hasconv, int hasvar); void clean(objliststruct *objlist, double clean_param, int *survives); @@ -64,7 +64,7 @@ PIXTYPE get_mean_thresh(infostruct *info, pliststruct *pixel); int convert_to_catalog(objliststruct *objlist, const int *survives, sep_catalog *cat, int64_t w, int include_pixels); -int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, +int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, int64_t w, int64_t h, int64_t bufw, int64_t bufh); void arraybuffer_readline(arraybuffer *buf); void arraybuffer_free(arraybuffer *buf); @@ -73,7 +73,7 @@ void arraybuffer_free(arraybuffer *buf); /* initialize buffer */ /* bufw must be less than or equal to w */ -int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, +int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, int64_t w, int64_t h, int64_t bufw, int64_t bufh) { int status; int64_t yl; @@ -325,7 +325,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (status != RETURN_OK) goto exit; } if (image->segmap) { - status = arraybuffer_init(&sbuf, image->segmap, image->sdtype, w, h, + status = arraybuffer_init(&sbuf, image->segmap, image->sdtype, w, h, stacksize, bufh); if (status != RETURN_OK) goto exit; @@ -364,8 +364,8 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, } else { objlist.nobj = 1; } - curpixinfo.pixnb = 1; - + curpixinfo.pixnb = 1; + /* Init finalobjlist */ QMALLOC(finalobjlist, objliststruct, 1, status); finalobjlist->obj = NULL; @@ -523,11 +523,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (PLISTEXIST(thresh)) { PLISTPIX(pixt, thresh) = thresh; }; - + if (idinfo[ididx].pixnb == 0) { idinfo[ididx].firstpix = prevpix*plistsize; idinfo[ididx].pixnb = 1; - } + } else if (idinfo[ididx].pixnb == image->idcounts[ididx]-1) { idinfo[ididx].pixnb++; idinfo[ididx].lastpix = prevpix*plistsize; @@ -536,7 +536,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, idinfo[ididx].pixnb++; }; break; - } + } } } } else { diff --git a/src/overlap.h b/src/overlap.h index 8db9449..7fe3f9a 100644 --- a/src/overlap.h +++ b/src/overlap.h @@ -320,7 +320,7 @@ static point circle_segment_single2(double x1, double y1, double x2, double y2) pt = (dx1 > dx2) ? pt2 : pt1; else pt = (dy1 > dy2) ? pt2 : pt1; - + return pt; } @@ -418,7 +418,7 @@ static double triangle_unitcircle_overlap(double x1, double y1, swap(&d1, &d3); } } - + /* Determine number of vertices inside circle */ in1 = d1 < 1.; in2 = d2 < 1.; diff --git a/tox.ini b/tox.ini index 535aec1..3d5c16b 100644 --- a/tox.ini +++ b/tox.ini @@ -7,4 +7,4 @@ isolated_build = true passenv = HOME deps = pytest astropy -commands = pytest test.py \ No newline at end of file +commands = pytest test.py From 17cee98b9aadb66375cdce5323a34941081d182d Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 31 Jan 2024 18:19:39 +0100 Subject: [PATCH 13/47] Fix tests. --- test.py | 440 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 249 insertions(+), 191 deletions(-) diff --git a/test.py b/test.py index 1cfb1f2..dea4079 100755 --- a/test.py +++ b/test.py @@ -1,20 +1,26 @@ #!/usr/bin/env py.test -from __future__ import print_function, division -# unicode_literals doesn't play well with numpy dtype field names +from __future__ import division, print_function import os -import pytest + import numpy as np -from numpy.testing import assert_allclose, assert_equal, assert_approx_equal +import pytest +from numpy.testing import assert_allclose, assert_approx_equal, assert_equal + import sep +# unicode_literals doesn't play well with numpy dtype field names + + # Try to import any FITS reader try: from fitsio import read as getdata + NO_FITS = False except: try: from astropy.io.fits import getdata + NO_FITS = False except: NO_FITS = True @@ -23,25 +29,27 @@ BACKIMAGE_FNAME = os.path.join("data", "back.fits") RMSIMAGE_FNAME = os.path.join("data", "rms.fits") IMAGECAT_FNAME = os.path.join("data", "image.cat") -IMAGECAT_DTYPE = [('number', np.int64), - ('x', np.float64), - ('y', np.float64), - ('xwin', np.float64), - ('ywin', np.float64), - ('x2', np.float64), - ('y2', np.float64), - ('xy', np.float64), - ('errx2', np.float64), - ('erry2', np.float64), - ('errxy', np.float64), - ('a', np.float64), - ('flux_aper', np.float64), - ('fluxerr_aper', np.float64), - ('kron_radius', np.float64), - ('flux_auto', np.float64), - ('fluxerr_auto', np.float64), - ('flux_radius', np.float64, (3,)), - ('flags', np.int64)] +IMAGECAT_DTYPE = [ + ("number", np.int64), + ("x", np.float64), + ("y", np.float64), + ("xwin", np.float64), + ("ywin", np.float64), + ("x2", np.float64), + ("y2", np.float64), + ("xy", np.float64), + ("errx2", np.float64), + ("erry2", np.float64), + ("errxy", np.float64), + ("a", np.float64), + ("flux_aper", np.float64), + ("fluxerr_aper", np.float64), + ("kron_radius", np.float64), + ("flux_auto", np.float64), + ("fluxerr_auto", np.float64), + ("flux_radius", np.float64, (3,)), + ("flags", np.int64), +] SUPPORTED_IMAGE_DTYPES = [np.float64, np.float32, np.int32] # If we have a FITS reader, read in the necessary test images @@ -54,6 +62,7 @@ # ----------------------------------------------------------------------------- # Helpers + def assert_allclose_structured(x, y): """Assert that two structured arrays are close. @@ -77,14 +86,16 @@ def matched_filter_snr(data, noise, kernel): sqrt(sum(kernel[i]^2 / noise[i]^2)) """ ctr = kernel.shape[0] // 2, kernel.shape[1] // 2 - kslice = ((0 - ctr[0], kernel.shape[0] - ctr[0]), # range in axis 0 - (0 - ctr[1], kernel.shape[1] - ctr[1])) # range in axis 1 + kslice = ( + (0 - ctr[0], kernel.shape[0] - ctr[0]), # range in axis 0 + (0 - ctr[1], kernel.shape[1] - ctr[1]), + ) # range in axis 1 out = np.empty_like(data) for y in range(data.shape[0]): - jmin = y + kslice[0][0] # min and max indicies to sum over + jmin = y + kslice[0][0] # min and max indicies to sum over jmax = y + kslice[0][1] - kjmin = 0 # min and max kernel indicies to sum over + kjmin = 0 # min and max kernel indicies to sum over kjmax = kernel.shape[0] # if we're over the edge of the image, limit extent @@ -98,9 +109,9 @@ def matched_filter_snr(data, noise, kernel): kjmax += offset for x in range(data.shape[1]): - imin = x + kslice[1][0] # min and max indicies to sum over + imin = x + kslice[1][0] # min and max indicies to sum over imax = x + kslice[1][1] - kimin = 0 # min and max kernel indicies to sum over + kimin = 0 # min and max kernel indicies to sum over kimax = kernel.shape[1] # if we're over the edge of the image, limit extent @@ -115,7 +126,7 @@ def matched_filter_snr(data, noise, kernel): d = data[jmin:jmax, imin:imax] n = noise[jmin:jmax, imin:imax] - w = 1. / n**2 + w = 1.0 / n**2 k = kernel[kjmin:kjmax, kimin:kimax] out[y, x] = np.sum(d * k * w) / np.sqrt(np.sum(k**2 * w)) @@ -125,6 +136,7 @@ def matched_filter_snr(data, noise, kernel): # ----------------------------------------------------------------------------- # Test versus Source Extractor results + @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_vs_sextractor(): """Test behavior of sep versus sextractor. @@ -144,51 +156,60 @@ def test_vs_sextractor(): # Test that SExtractor background is same as SEP: bkgarr = bkg.back(dtype=np.float32) - assert_allclose(bkgarr, image_refback, rtol=1.e-5) + assert_allclose(bkgarr, image_refback, rtol=1.0e-5) - # Test that SExtractor background rms is same as SEP: + # Test that SExtractor background rms is same as SEP: rmsarr = bkg.rms(dtype=np.float32) - assert_allclose(rmsarr, image_refrms, rtol=1.e-4) + assert_allclose(rmsarr, image_refrms, rtol=1.0e-4) # Extract objects (use deblend_cont=1.0 to disable deblending). bkg.subfrom(data) objs = sep.extract(data, 1.5, err=bkg.globalrms, deblend_cont=1.0) - objs = np.sort(objs, order=['y']) + objs = np.sort(objs, order=["y"]) # Read SExtractor result refobjs = np.loadtxt(IMAGECAT_FNAME, dtype=IMAGECAT_DTYPE) - refobjs = np.sort(refobjs, order=['y']) + refobjs = np.sort(refobjs, order=["y"]) # Found correct number of sources at the right locations? - assert_allclose(objs['x'], refobjs['x'] - 1., atol=1.e-3) - assert_allclose(objs['y'], refobjs['y'] - 1., atol=1.e-3) + assert_allclose(objs["x"], refobjs["x"] - 1.0, atol=1.0e-3) + assert_allclose(objs["y"], refobjs["y"] - 1.0, atol=1.0e-3) # Correct Variance and Variance Errors? - assert_allclose(objs['x2'], refobjs['x2'], atol=1.e-4) - assert_allclose(objs['y2'], refobjs['y2'], atol=1.e-4) - assert_allclose(objs['xy'], refobjs['xy'], atol=1.e-4) - assert_allclose(objs['errx2'], refobjs['errx2'], rtol=1.e-4) - assert_allclose(objs['erry2'], refobjs['erry2'], rtol=1.e-4) - assert_allclose(objs['errxy'], refobjs['errxy'], rtol=1.e-3) + assert_allclose(objs["x2"], refobjs["x2"], atol=1.0e-4) + assert_allclose(objs["y2"], refobjs["y2"], atol=1.0e-4) + assert_allclose(objs["xy"], refobjs["xy"], atol=1.0e-4) + assert_allclose(objs["errx2"], refobjs["errx2"], rtol=1.0e-4) + assert_allclose(objs["erry2"], refobjs["erry2"], rtol=1.0e-4) + assert_allclose(objs["errxy"], refobjs["errxy"], rtol=1.0e-3) # Test aperture flux - flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 5., - err=bkg.globalrms) - assert_allclose(flux, refobjs['flux_aper'], rtol=2.e-4) - assert_allclose(fluxerr, refobjs['fluxerr_aper'], rtol=1.0e-5) + flux, fluxerr, flag = sep.sum_circle( + data, objs["x"], objs["y"], 5.0, err=bkg.globalrms + ) + assert_allclose(flux, refobjs["flux_aper"], rtol=2.0e-4) + assert_allclose(fluxerr, refobjs["fluxerr_aper"], rtol=1.0e-5) # check if the flags work at all (comparison values assert ((flag & sep.APER_TRUNC) != 0).sum() == 4 assert ((flag & sep.APER_HASMASKED) != 0).sum() == 0 # Test "flux_auto" - kr, flag = sep.kron_radius(data, objs['x'], objs['y'], objs['a'], - objs['b'], objs['theta'], 6.0) - - flux, fluxerr, flag = sep.sum_ellipse(data, objs['x'], objs['y'], - objs['a'], objs['b'], - objs['theta'], r=2.5 * kr, - err=bkg.globalrms, subpix=1) + kr, flag = sep.kron_radius( + data, objs["x"], objs["y"], objs["a"], objs["b"], objs["theta"], 6.0 + ) + + flux, fluxerr, flag = sep.sum_ellipse( + data, + objs["x"], + objs["y"], + objs["a"], + objs["b"], + objs["theta"], + r=2.5 * kr, + err=bkg.globalrms, + subpix=1, + ) # For some reason, one object doesn't match. It's very small # and kron_radius is set to 0.0 in SExtractor, but 0.08 in sep. @@ -201,59 +222,74 @@ def test_vs_sextractor(): # We use atol for radius because it is reported to nearest 0.01 in # reference objects. - assert_allclose(2.5*kr, refobjs['kron_radius'], atol=0.01, rtol=0.) - assert_allclose(flux, refobjs['flux_auto'], rtol=0.0005) - assert_allclose(fluxerr, refobjs['fluxerr_auto'], rtol=0.0005) + assert_allclose(2.5 * kr, refobjs["kron_radius"], atol=0.01, rtol=0.0) + assert_allclose(flux, refobjs["flux_auto"], rtol=0.0005) + assert_allclose(fluxerr, refobjs["fluxerr_auto"], rtol=0.0005) # Test using a mask in kron_radius and sum_ellipse. for dtype in [np.bool_, np.int32, np.float32, np.float64]: mask = np.zeros_like(data, dtype=dtype) - kr2, flag = sep.kron_radius(data, objs['x'], objs['y'], - objs['a'], objs['b'], objs['theta'], - 6.0, mask=mask) - kr2[i] = 0. + kr2, flag = sep.kron_radius( + data, + objs["x"], + objs["y"], + objs["a"], + objs["b"], + objs["theta"], + 6.0, + mask=mask, + ) + kr2[i] = 0.0 assert np.all(kr == kr2) # Test ellipse representation conversion - cxx, cyy, cxy = sep.ellipse_coeffs(objs['a'], objs['b'], objs['theta']) - assert_allclose(cxx, objs['cxx'], rtol=1.e-4) - assert_allclose(cyy, objs['cyy'], rtol=1.e-4) - assert_allclose(cxy, objs['cxy'], rtol=1.e-4) + cxx, cyy, cxy = sep.ellipse_coeffs(objs["a"], objs["b"], objs["theta"]) + assert_allclose(cxx, objs["cxx"], rtol=1.0e-4) + assert_allclose(cyy, objs["cyy"], rtol=1.0e-4) + assert_allclose(cxy, objs["cxy"], rtol=1.0e-4) - a, b, theta = sep.ellipse_axes(objs['cxx'], objs['cyy'], objs['cxy']) - assert_allclose(a, objs['a'], rtol=1.e-4) - assert_allclose(b, objs['b'], rtol=1.e-4) - assert_allclose(theta, objs['theta'], rtol=1.e-4) + a, b, theta = sep.ellipse_axes(objs["cxx"], objs["cyy"], objs["cxy"]) + assert_allclose(a, objs["a"], rtol=1.0e-4) + assert_allclose(b, objs["b"], rtol=1.0e-4) + assert_allclose(theta, objs["theta"], rtol=1.0e-4) - #test round trip + # test round trip cxx, cyy, cxy = sep.ellipse_coeffs(a, b, theta) - assert_allclose(cxx, objs['cxx'], rtol=1.e-4) - assert_allclose(cyy, objs['cyy'], rtol=1.e-4) - assert_allclose(cxy, objs['cxy'], rtol=1.e-4) + assert_allclose(cxx, objs["cxx"], rtol=1.0e-4) + assert_allclose(cyy, objs["cyy"], rtol=1.0e-4) + assert_allclose(cxy, objs["cxy"], rtol=1.0e-4) # test flux_radius - fr, flags = sep.flux_radius(data, objs['x'], objs['y'], 6.*refobjs['a'], - [0.1, 0.5, 0.6], normflux=refobjs['flux_auto'], - subpix=5) + fr, flags = sep.flux_radius( + data, + objs["x"], + objs["y"], + 6.0 * refobjs["a"], + [0.1, 0.5, 0.6], + normflux=refobjs["flux_auto"], + subpix=5, + ) assert_allclose(fr, refobjs["flux_radius"], rtol=0.04, atol=0.01) # test winpos - sig = 2. / 2.35 * fr[:, 1] # flux_radius = 0.5 - xwin, ywin, flag = sep.winpos(data, objs['x'], objs['y'], sig) - assert_allclose(xwin, refobjs["xwin"] - 1., rtol=0., atol=0.0015) - assert_allclose(ywin, refobjs["ywin"] - 1., rtol=0., atol=0.0015) + sig = 2.0 / 2.35 * fr[:, 1] # flux_radius = 0.5 + xwin, ywin, flag = sep.winpos(data, objs["x"], objs["y"], sig) + assert_allclose(xwin, refobjs["xwin"] - 1.0, rtol=0.0, atol=0.0015) + assert_allclose(ywin, refobjs["ywin"] - 1.0, rtol=0.0, atol=0.0015) + # ----------------------------------------------------------------------------- # Background + def test_masked_background(): - data = 0.1 * np.ones((6,6)) - data[1,1] = 1. - data[4,1] = 1. - data[1,4] = 1. - data[4,4] = 1. + data = 0.1 * np.ones((6, 6)) + data[1, 1] = 1.0 + data[4, 1] = 1.0 + data[1, 4] = 1.0 + data[4, 4] = 1.0 - mask = np.zeros((6,6), dtype=np.bool_) + mask = np.zeros((6, 6), dtype=np.bool_) # Background array without mask sky = sep.Background(data, bw=3, bh=3, fw=1, fh=1) @@ -312,9 +348,11 @@ def test_background_rms(): assert rms.dtype == np.float64 assert rms.shape == (ny, nx) + # ----------------------------------------------------------------------------- # Extract + @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_extract_with_noise_array(): @@ -329,10 +367,14 @@ def test_extract_with_noise_array(): # convolved. Near edges, the convolution doesn't adjust for pixels # off edge boundaries. As a result, the convolved noise map is not # all ones. - objects = sep.extract(data, 1.5*bkg.globalrms, filter_kernel=None) - objects2 = sep.extract(data, 1.5*bkg.globalrms, err=np.ones_like(data), - filter_kernel=None) - names_to_remove = ['errx2', 'erry2', 'errxy'] + objects = sep.extract(data, 1.5 * bkg.globalrms, filter_kernel=None) + objects2 = sep.extract( + data, 1.5 * bkg.globalrms, err=np.ones_like(data), filter_kernel=None + ) + + # Threshold must be removed, as it is calculated differently in variable + # noise situations - see PR#146 for more details + names_to_remove = ["errx2", "erry2", "errxy", "thresh"] names_to_keep = [i for i in objects.dtype.names if i not in names_to_remove] objects = objects[names_to_keep] objects2 = objects2[names_to_keep] @@ -343,7 +385,7 @@ def test_extract_with_noise_array(): noise = bkg.globalrms * np.ones_like(data) objects2 = sep.extract(data, 1.5, err=noise, filter_kernel=None) - names_to_remove = ['errx2', 'erry2', 'errxy'] + names_to_remove = ["errx2", "erry2", "errxy", "thresh"] names_to_keep = [i for i in objects.dtype.names if i not in names_to_remove] objects = objects[names_to_keep] objects2 = objects2[names_to_keep] @@ -363,15 +405,15 @@ def test_extract_with_noise_convolution(): error = np.ones((20, 20)) # Add some noise representing bad pixels. We do not want to detect these. - image[17, 3] = 100. - error[17, 3] = 100. - image[10, 0] = 100. - error[10, 0] = 100. - image[17, 17] = 100. - error[17, 17] = 100. + image[17, 3] = 100.0 + error[17, 3] = 100.0 + image[10, 0] = 100.0 + error[10, 0] = 100.0 + image[17, 17] = 100.0 + error[17, 17] = 100.0 # Add some real point sources that we should find. - image[3, 17] = 10. + image[3, 17] = 10.0 image[6, 6] = 2.0 image[7, 6] = 1.0 @@ -380,16 +422,16 @@ def test_extract_with_noise_convolution(): image[6, 7] = 1.0 objects = sep.extract(image, 2.0, minarea=1, err=error) - objects.sort(order=['x', 'y']) + objects.sort(order=["x", "y"]) # Check that we recovered the two correct objects and not the others. assert len(objects) == 2 - assert_approx_equal(objects[0]['x'], 6.) - assert_approx_equal(objects[0]['y'], 6.) + assert_approx_equal(objects[0]["x"], 6.0) + assert_approx_equal(objects[0]["y"], 6.0) - assert_approx_equal(objects[1]['x'], 17.) - assert_approx_equal(objects[1]['y'], 3.) + assert_approx_equal(objects[1]["x"], 17.0) + assert_approx_equal(objects[1]["y"], 3.0) def test_extract_matched_filter_at_edge(): @@ -398,14 +440,18 @@ def test_extract_matched_filter_at_edge(): data = np.zeros((20, 20)) err = np.ones_like(data) - kernel = np.array([[1., 2., 1.], - [2., 4., 2.], - [1., 2., 1.]]) + kernel = np.array([[1.0, 2.0, 1.0], [2.0, 4.0, 2.0], [1.0, 2.0, 1.0]]) data[18:20, 9:12] = kernel[0:2, :] - objects, pix = sep.extract(data, 2.0, err=err, filter_kernel=kernel, - filter_type="matched", segmentation_map=True) + objects, pix = sep.extract( + data, + 2.0, + err=err, + filter_kernel=kernel, + filter_type="matched", + segmentation_map=True, + ) assert len(objects) == 1 assert objects["npix"][0] == 6 @@ -421,14 +467,14 @@ def test_extract_with_mask(): # mask half the image ylim = data.shape[0] // 2 mask = np.zeros(data.shape, dtype=np.bool_) - mask[ylim:,:] = True + mask[ylim:, :] = True - objects = sep.extract(data, 1.5*bkg.globalrms, mask=mask) + objects = sep.extract(data, 1.5 * bkg.globalrms, mask=mask) # check that we found some objects and that they are all in the unmasked # region. assert len(objects) > 0 - assert np.all(objects['y'] < ylim) + assert np.all(objects["y"] < ylim) @pytest.mark.skipif(NO_FITS, reason="no FITS reader") @@ -439,28 +485,28 @@ def test_extract_segmentation_map(): bkg = sep.Background(data, bw=64, bh=64, fw=3, fh=3) bkg.subfrom(data) - objects, segmap = sep.extract(data, 1.5*bkg.globalrms, - segmentation_map=True) + objects, segmap = sep.extract(data, 1.5 * bkg.globalrms, segmentation_map=True) assert type(segmap) is np.ndarray assert segmap.shape == data.shape for i in range(len(objects)): - assert objects["npix"][i] == (segmap == i+1).sum() + assert objects["npix"][i] == (segmap == i + 1).sum() # ----------------------------------------------------------------------------- # aperture tests naper = 1000 -x = np.random.uniform(200., 800., naper) -y = np.random.uniform(200., 800., naper) +x = np.random.uniform(200.0, 800.0, naper) +y = np.random.uniform(200.0, 800.0, naper) data_shape = (1000, 1000) + def test_aperture_dtypes(): """Ensure that all supported image dtypes work in sum_circle() and give the same answer""" - r = 3. + r = 3.0 fluxes = [] for dt in SUPPORTED_IMAGE_DTYPES: @@ -477,61 +523,60 @@ def test_apertures_small_ellipse_exact(): data = np.ones(data_shape) r = 0.3 - rtol=1.e-10 - flux, fluxerr, flag = sep.sum_ellipse(data, x, x, r, r, 0., subpix=0) - assert_allclose(flux, np.pi*r**2, rtol=rtol) + rtol = 1.0e-10 + flux, fluxerr, flag = sep.sum_ellipse(data, x, x, r, r, 0.0, subpix=0) + assert_allclose(flux, np.pi * r**2, rtol=rtol) + def test_apertures_all(): """Test that aperture subpixel sampling works""" data = np.random.rand(*data_shape) - r = 3. - rtol=1.e-8 + r = 3.0 + rtol = 1.0e-8 for subpix in [0, 1, 5]: - flux_ref, fluxerr_ref, flag_ref = sep.sum_circle(data, x, y, r, - subpix=subpix) + flux_ref, fluxerr_ref, flag_ref = sep.sum_circle(data, x, y, r, subpix=subpix) - flux, fluxerr, flag = sep.sum_circann(data, x, y, 0., r, - subpix=subpix) + flux, fluxerr, flag = sep.sum_circann(data, x, y, 0.0, r, subpix=subpix) assert_allclose(flux, flux_ref, rtol=rtol) - flux, fluxerr, flag = sep.sum_ellipse(data, x, y, r, r, 0., - subpix=subpix) + flux, fluxerr, flag = sep.sum_ellipse(data, x, y, r, r, 0.0, subpix=subpix) assert_allclose(flux, flux_ref, rtol=rtol) - flux, fluxerr, flag = sep.sum_ellipse(data, x, y, 1., 1., 0., r=r, - subpix=subpix) + flux, fluxerr, flag = sep.sum_ellipse( + data, x, y, 1.0, 1.0, 0.0, r=r, subpix=subpix + ) assert_allclose(flux, flux_ref, rtol=rtol) - def test_apertures_exact(): """Test area as measured by exact aperture modes on array of ones""" - theta = np.random.uniform(-np.pi/2., np.pi/2., naper) + theta = np.random.uniform(-np.pi / 2.0, np.pi / 2.0, naper) ratio = np.random.uniform(0.2, 1.0, naper) - r = 3. + r = 3.0 for dt in SUPPORTED_IMAGE_DTYPES: data = np.ones(data_shape, dtype=dt) - for r in [0.5, 1., 3.]: + for r in [0.5, 1.0, 3.0]: flux, fluxerr, flag = sep.sum_circle(data, x, y, r, subpix=0) - assert_allclose(flux, np.pi*r**2) + assert_allclose(flux, np.pi * r**2) - rout = r*1.1 - flux, fluxerr, flag = sep.sum_circann(data, x, y, r, rout, - subpix=0) - assert_allclose(flux, np.pi*(rout**2 - r**2)) + rout = r * 1.1 + flux, fluxerr, flag = sep.sum_circann(data, x, y, r, rout, subpix=0) + assert_allclose(flux, np.pi * (rout**2 - r**2)) - flux, fluxerr, flag = sep.sum_ellipse(data, x, y, 1., ratio, - theta, r=r, subpix=0) - assert_allclose(flux, np.pi*ratio*r**2) + flux, fluxerr, flag = sep.sum_ellipse( + data, x, y, 1.0, ratio, theta, r=r, subpix=0 + ) + assert_allclose(flux, np.pi * ratio * r**2) - rout = r*1.1 - flux, fluxerr, flag = sep.sum_ellipann(data, x, y, 1., ratio, - theta, r, rout, subpix=0) - assert_allclose(flux, np.pi*ratio*(rout**2 - r**2)) + rout = r * 1.1 + flux, fluxerr, flag = sep.sum_ellipann( + data, x, y, 1.0, ratio, theta, r, rout, subpix=0 + ) + assert_allclose(flux, np.pi * ratio * (rout**2 - r**2)) def test_aperture_bkgann_overlapping(): @@ -540,55 +585,57 @@ def test_aperture_bkgann_overlapping(): # If bkgann overlaps aperture exactly, result should be zero # (with subpix=1) data = np.random.rand(*data_shape) - r = 5. - f, _, _ = sep.sum_circle(data, x, y, r, bkgann=(0., r), subpix=1) - assert_allclose(f, 0., rtol=0., atol=1.e-13) + r = 5.0 + f, _, _ = sep.sum_circle(data, x, y, r, bkgann=(0.0, r), subpix=1) + assert_allclose(f, 0.0, rtol=0.0, atol=1.0e-13) - f, _, _ = sep.sum_ellipse(data, x, y, 2., 1., np.pi/4., r=r, - bkgann=(0., r), subpix=1) - assert_allclose(f, 0., rtol=0., atol=1.e-13) + f, _, _ = sep.sum_ellipse( + data, x, y, 2.0, 1.0, np.pi / 4.0, r=r, bkgann=(0.0, r), subpix=1 + ) + assert_allclose(f, 0.0, rtol=0.0, atol=1.0e-13) def test_aperture_bkgann_ones(): """Test bkgann functionality with flat data""" data = np.ones(data_shape) - r=5. - bkgann=(6., 8.) + r = 5.0 + bkgann = (6.0, 8.0) # On flat data, result should be zero for any bkgann and subpix - f, fe, _ = sep.sum_circle(data, x, y, r, bkgann=bkgann, gain=1.) - assert_allclose(f, 0., rtol=0., atol=1.e-13) + f, fe, _ = sep.sum_circle(data, x, y, r, bkgann=bkgann, gain=1.0) + assert_allclose(f, 0.0, rtol=0.0, atol=1.0e-13) # for all ones data and no error array, error should be close to # sqrt(Npix_aper + Npix_ann * (Npix_aper**2 / Npix_ann**2)) aper_area = np.pi * r**2 - bkg_area = np.pi * (bkgann[1]**2 - bkgann[0]**2) - expected_error = np.sqrt(aper_area + bkg_area * (aper_area/bkg_area)**2) + bkg_area = np.pi * (bkgann[1] ** 2 - bkgann[0] ** 2) + expected_error = np.sqrt(aper_area + bkg_area * (aper_area / bkg_area) ** 2) assert_allclose(fe, expected_error, rtol=0.1) - f, _, _ = sep.sum_ellipse(data, x, y, 2., 1., np.pi/4., r, bkgann=bkgann) - assert_allclose(f, 0., rtol=0., atol=1.e-13) + f, _, _ = sep.sum_ellipse(data, x, y, 2.0, 1.0, np.pi / 4.0, r, bkgann=bkgann) + assert_allclose(f, 0.0, rtol=0.0, atol=1.0e-13) + def test_masked_segmentation_measurements(): """Test measurements with segmentation masking""" NX = 100 - data = np.zeros((NX*2,NX*2)) + data = np.zeros((NX * 2, NX * 2)) yp, xp = np.indices(data.shape) #### # Make two 2D gaussians that slightly overlap # width of the 2D objects - gsigma = 10. + gsigma = 10.0 # offset between two gaussians in sigmas off = 4 - for xy in [[NX,NX], [NX+off*gsigma, NX+off*gsigma]]: - R = np.sqrt((xp-xy[0])**2+(yp-xy[1])**2) - g_i = np.exp(-R**2/2/gsigma**2) + for xy in [[NX, NX], [NX + off * gsigma, NX + off * gsigma]]: + R = np.sqrt((xp - xy[0]) ** 2 + (yp - xy[1]) ** 2) + g_i = np.exp(-(R**2) / 2 / gsigma**2) data += g_i # Absolute total @@ -597,65 +644,75 @@ def test_masked_segmentation_measurements(): # Add some noise rms = 0.02 np.random.seed(1) - data += np.random.normal(size=data.shape)*rms + data += np.random.normal(size=data.shape) * rms # Run source detection - objs, segmap = sep.extract(data, thresh=1.2, err=rms, mask=None, - segmentation_map=True) + objs, segmap = sep.extract( + data, thresh=1.2, err=rms, mask=None, segmentation_map=True + ) - seg_id = np.arange(1, len(objs)+1, dtype=np.int32) + seg_id = np.arange(1, len(objs) + 1, dtype=np.int32) # Compute Kron/Auto parameters - x, y, a, b = objs['x'], objs['y'], objs['a'], objs['b'] - theta = objs['theta'] + x, y, a, b = objs["x"], objs["y"], objs["a"], objs["b"] + theta = objs["theta"] kronrad, krflag = sep.kron_radius(data, x, y, a, b, theta, 6.0) - flux_auto, fluxerr, flag = sep.sum_ellipse(data, x, y, a, b, theta, - 2.5*kronrad, - segmap=segmap, seg_id=seg_id, - subpix=1) + flux_auto, fluxerr, flag = sep.sum_ellipse( + data, x, y, a, b, theta, 2.5 * kronrad, segmap=segmap, seg_id=seg_id, subpix=1 + ) # Test total flux - assert_allclose(flux_auto, total_exact, rtol=5.e-2) + assert_allclose(flux_auto, total_exact, rtol=5.0e-2) # Flux_radius for flux_fraction in [0.2, 0.5]: # Exact solution - rhalf_exact = np.sqrt(-np.log(1-flux_fraction)*gsigma**2*2) + rhalf_exact = np.sqrt(-np.log(1 - flux_fraction) * gsigma**2 * 2) # Masked measurement - flux_radius, flag = sep.flux_radius(data, x, y, 6.*a, flux_fraction, - seg_id=seg_id, segmap=segmap, - normflux=flux_auto, subpix=5) + flux_radius, flag = sep.flux_radius( + data, + x, + y, + 6.0 * a, + flux_fraction, + seg_id=seg_id, + segmap=segmap, + normflux=flux_auto, + subpix=5, + ) # Test flux fraction - assert_allclose(flux_radius, rhalf_exact, rtol=5.e-2) + assert_allclose(flux_radius, rhalf_exact, rtol=5.0e-2) if False: - print('test_masked_flux_radius') + print("test_masked_flux_radius") print(total_exact, flux_auto) print(rhalf_exact, flux_radius) + def test_mask_ellipse(): arr = np.zeros((20, 20), dtype=np.bool_) # should mask 5 pixels: - sep.mask_ellipse(arr, 10., 10., 1.0, 1.0, 0.0, r=1.001) + sep.mask_ellipse(arr, 10.0, 10.0, 1.0, 1.0, 0.0, r=1.001) assert arr.sum() == 5 # should mask 13 pixels: - sep.mask_ellipse(arr, 10., 10., 1.0, 1.0, 0.0, r=2.001) + sep.mask_ellipse(arr, 10.0, 10.0, 1.0, 1.0, 0.0, r=2.001) assert arr.sum() == 13 def test_flux_radius(): data = np.ones(data_shape) - fluxfrac = [0.2**2, 0.3**2, 0.7**2, 1.] - true_r = [2., 3., 7., 10.] - r, _ = sep.flux_radius(data, x, y, 10.*np.ones_like(x), - [0.2**2, 0.3**2, 0.7**2, 1.], subpix=5) + fluxfrac = [0.2**2, 0.3**2, 0.7**2, 1.0] + true_r = [2.0, 3.0, 7.0, 10.0] + r, _ = sep.flux_radius( + data, x, y, 10.0 * np.ones_like(x), [0.2**2, 0.3**2, 0.7**2, 1.0], subpix=5 + ) for i in range(len(fluxfrac)): assert_allclose(r[:, i], true_r[i], rtol=0.01) @@ -665,17 +722,18 @@ def test_mask_ellipse_alt(): arr = np.zeros((20, 20), dtype=np.bool_) # should mask 5 pixels: - sep.mask_ellipse(arr, 10., 10., cxx=1.0, cyy=1.0, cxy=0.0, r=1.001) + sep.mask_ellipse(arr, 10.0, 10.0, cxx=1.0, cyy=1.0, cxy=0.0, r=1.001) assert arr.sum() == 5 # should mask 13 pixels: - sep.mask_ellipse(arr, 10., 10., cxx=1.0, cyy=1.0, cxy=0.0, r=2.001) + sep.mask_ellipse(arr, 10.0, 10.0, cxx=1.0, cyy=1.0, cxy=0.0, r=2.001) assert arr.sum() == 13 # ----------------------------------------------------------------------------- # General behavior and utilities + def test_byte_order_exception(): """Test that error about byte order is raised with non-native byte order input array. This should happen for Background, extract, @@ -685,7 +743,7 @@ def test_byte_order_exception(): data = data.byteswap(True).newbyteorder() with pytest.raises(ValueError) as excinfo: bkg = sep.Background(data) - assert 'byte order' in excinfo.value.args[0] + assert "byte order" in excinfo.value.args[0] def test_set_pixstack(): From 1957a93f5371a4aa03f5d23b96cb9b04b5f9d0d1 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:51:14 +0100 Subject: [PATCH 14/47] Change name, add docs dependencies. --- .gitignore | 1 + .pre-commit-config.yaml | 6 ++++++ pyproject.toml | 13 ++++++++++++- sep.pyx => sep_pjw.pyx | 12 ++++++------ setup.py | 4 ++-- 5 files changed, 27 insertions(+), 9 deletions(-) rename sep.pyx => sep_pjw.pyx (99%) diff --git a/.gitignore b/.gitignore index c4f278c..ac8cb22 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ MANIFEST # generated C file sep.c +sep_pjw.c # PyCharm .idea diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a652d65..147760e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,3 +20,9 @@ repos: # rev: v1.6.0 # hooks: # - id: numpydoc-validation +- repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v0.9.1 + hooks: + - id: sphinx-lint + args: [--enable=all, --disable=default-role, --max-line-length=75] + files: ^docs/ diff --git a/pyproject.toml b/pyproject.toml index 51b2cf9..c53aaf7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=61.0", "wheel", "oldest-supported-numpy", "Cython", "se build-backend = "setuptools.build_meta" [project] -name = "sep" +name = "sep-pjw" dynamic = ["version"] authors = [ { name="Emmanuel Bertin"}, @@ -37,6 +37,17 @@ dependencies = [ "numpy>=1.23" ] +[project.optional-dependencies] +docs = [ + "sphinx-copybutton>=0.5", + "sphinx>=7.2", + "numpydoc>=1.6", + "furo>=2024.1", + "nbsphinx>=0.9.3", + "pandoc>=2.3", + "fitsio>=1.2.1", +] + [project.urls] "Homepage" = "https://github.com/PJ-Watson/sep" "Bug Tracker" = "https://github.com/PJ-Watson/sep/issues" diff --git a/sep.pyx b/sep_pjw.pyx similarity index 99% rename from sep.pyx rename to sep_pjw.pyx index ab71a4b..2482b31 100644 --- a/sep.pyx +++ b/sep_pjw.pyx @@ -899,7 +899,7 @@ def sum_circle(np.ndarray data not None, x, y, r, and seg[j, i] != 0``). If ``seg_id`` is negative, all pixels other than those belonging to the object of interest are masked. (Pixel ``j, i`` is masked if ``seg[j, i] != -seg_id``). NB: must be included if - ``segmap` is provided. + ``segmap`` is provided. bkgann : tuple, optional Length 2 tuple giving the inner and outer radius of a @@ -1096,7 +1096,7 @@ def sum_circann(np.ndarray data not None, x, y, rin, rout, and seg[j, i] != 0``). If ``seg_id`` is negative, all pixels other than those belonging to the object of interest are masked. (Pixel ``j, i`` is masked if ``seg[j, i] != -seg_id``). NB: must be included if - ``segmap` is provided. + ``segmap`` is provided. gain : float, optional Conversion factor between data array units and poisson counts, @@ -1241,7 +1241,7 @@ def sum_ellipse(np.ndarray data not None, x, y, a, b, theta, r=1.0, and seg[j, i] != 0``). If ``seg_id`` is negative, all pixels other than those belonging to the object of interest are masked. (Pixel ``j, i`` is masked if ``seg[j, i] != -seg_id``). NB: must be included if - ``segmap` is provided. + ``segmap`` is provided. bkgann : tuple, optional Length 2 tuple giving the inner and outer radius of a @@ -1455,7 +1455,7 @@ def sum_ellipann(np.ndarray data not None, x, y, a, b, theta, rin, rout, and seg[j, i] != 0``). If ``seg_id`` is negative, all pixels other than those belonging to the object of interest are masked. (Pixel ``j, i`` is masked if ``seg[j, i] != -seg_id``). NB: must be included if - ``segmap` is provided. + ``segmap`` is provided. subpix : int, optional Subpixel sampling factor. Default is 5. @@ -1594,7 +1594,7 @@ def flux_radius(np.ndarray data not None, x, y, rmax, frac, normflux=None, and seg[j, i] != 0``). If ``seg_id`` is negative, all pixels other than those belonging to the object of interest are masked. (Pixel ``j, i`` is masked if ``seg[j, i] != -seg_id``). NB: must be included if - ``segmap` is provided. + ``segmap`` is provided. subpix : int, optional Subpixel sampling factor. Default is 5. @@ -1849,7 +1849,7 @@ def kron_radius(np.ndarray data not None, x, y, a, b, theta, r, and seg[j, i] != 0``). If ``seg_id`` is negative, all pixels other than those belonging to the object of interest are masked. (Pixel ``j, i`` is masked if ``seg[j, i] != -seg_id``). NB: must be included if - ``segmap` is provided. + ``segmap`` is provided. Returns ------- diff --git a/setup.py b/setup.py index 267d870..67543cb 100755 --- a/setup.py +++ b/setup.py @@ -26,12 +26,12 @@ import numpy from Cython.Build import cythonize - sourcefiles = ["sep.pyx"] + glob(os.path.join("src", "*.c")) + sourcefiles = ["sep_pjw.pyx"] + glob(os.path.join("src", "*.c")) headerfiles = glob(os.path.join("src", "*.h")) include_dirs = [numpy.get_include(), "src"] extensions = [ Extension( - "sep", + "sep_pjw", sourcefiles, include_dirs=include_dirs, depends=headerfiles, From a80e2b93ed9c670e2c6a453034bcf31f440b7df4 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:54:48 +0100 Subject: [PATCH 15/47] Rename functions and reformat docs. Aim for consistent line length of 75chars in docs. Fix missing explanation for byteswap. Move to 'python -m pip' rather than 'pip'. --- docs/apertures.rst | 88 +++++++++++++++++++++++++++++++-------------- docs/conf.py | 23 ++++++------ docs/filter.rst | 56 +++++++++++++++++------------ docs/index.rst | 43 ++++++++++++++-------- docs/reference.rst | 54 ++++++++++++++-------------- docs/tutorial.ipynb | 9 +++-- 6 files changed, 169 insertions(+), 104 deletions(-) diff --git a/docs/apertures.rst b/docs/apertures.rst index 03c1d67..eef9258 100644 --- a/docs/apertures.rst +++ b/docs/apertures.rst @@ -3,17 +3,19 @@ Aperture photometry There are four aperture functions available: -================== ========================= -Function Sums data within... -================== ========================= -`sep.sum_circle` circle(s) -`sep.sum_circann` circular annulus/annuli -`sep.sum_ellipse` ellipse(s) -`sep.sum_ellipann` elliptical annulus/annuli -================== ========================= +====================== ========================= +Function Sums data within... +====================== ========================= +`sep_pjw.sum_circle` circle(s) +`sep_pjw.sum_circann` circular annulus/annuli +`sep_pjw.sum_ellipse` ellipse(s) +`sep_pjw.sum_ellipann` elliptical annulus/annuli +====================== ========================= The follow examples demonstrate options for circular aperture -photometry. The other functions behave similarly. :: +photometry. The other functions behave similarly. + +.. code-block:: python # sum flux in circles of radius=3.0 flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 3.0) @@ -35,7 +37,9 @@ flexibly and efficiently calculated, depending on the characteristics of your data. The presence of an ``err`` or ``var`` keyword indicates a per-pixel noise, while the presense of a ``gain`` keyword indicates that the Poisson uncertainty on the total sum should be included. Some -illustrative examples:: +illustrative examples: + +.. code-block:: python # Specify a per-pixel "background" error and a gain. This is suitable # when the data have been background subtracted. @@ -76,17 +80,22 @@ noise in each pixel, :math:`F` is the sum in the aperture and **Masking** Apply a mask (same shape as data). Pixels where the mask is True are -"corrected" to the average value within the aperture. :: +"corrected" to the average value within the aperture. + +.. code-block:: python flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 3.0, mask=mask) **Local background subtraction** -The `~sep.sum_circle` and `~sep.sum_ellipse` functions have options -for performing local background subtraction. For example, to subtract the background calculated in an annulus between 6 and 8 pixel radius:: +The `~sep_pjw.sum_circle` and `~sep_pjw.sum_ellipse` functions have options +for performing local background subtraction. For example, to subtract the +background calculated in an annulus between 6 and 8 pixel radius: - flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 3.0, +.. code-block:: python + + flux, fluxerr, flag = sep_pjw.sum_circle(data, objs['x'], objs['y'], 3.0, mask=mask, bkgann=(6., 8.)) Pixels in the background annulus are not subsampled and any masked @@ -98,7 +107,9 @@ Equivalent of FLUX_AUTO (e.g., MAG_AUTO) in Source Extractor ------------------------------------------------------------ This is a two-step process. First we calculate the Kron radius for each -object, then we perform elliptical aperture photometry within that radius:: +object, then we perform elliptical aperture photometry within that radius: + +.. code-block:: python kronrad, krflag = sep.kron_radius(data, x, y, a, b, theta, 6.0) flux, fluxerr, flag = sep.sum_ellipse(data, x, y, a, b, theta, 2.5*kronrad, @@ -110,7 +121,9 @@ This specific example is the equilvalent of setting ``PHOT_AUTOPARAMS ``sep.sum_ellipse``). The second Source Extractor parameter is a minimum diameter. To replicate Source Extractor behavior for non-zero values of the minimum diameter, one would put in logic to use circular -aperture photometry if the Kron radius is too small. For example:: +aperture photometry if the Kron radius is too small. For example: + +.. code-block:: python r_min = 1.75 # minimum diameter = 3.5 use_circle = kronrad * np.sqrt(a * b) < r_min @@ -134,12 +147,16 @@ the circle containing the same flux as whatever ellipse Source Extractor used for ``FLUX_AUTO``. Given a previous calculation of ``flux`` as above, calculate the -radius for a flux fraction of 0.5:: +radius for a flux fraction of 0.5: + +.. code-block:: python r, flag = sep.flux_radius(data, objs['x'], objs['y'], 6.*objs['a'], 0.5, normflux=flux, subpix=5) -And for multiple flux fractions:: +And for multiple flux fractions: + +.. code-block:: python r, flag = sep.flux_radius(data, objs['x'], objs['y'], 6.*objs['a'], [0.5, 0.6], normflux=flux, subpix=5) @@ -155,7 +172,9 @@ Extractor exactly, the right ``sig`` parameter (giving a description of the effective width) must be used for each object. Source Extractor uses ``2. / 2.35 * (half-light radius)`` where the half-light radius is calculated using ``flux_radius`` with a fraction -of 0.5 and a normalizing flux of ``FLUX_AUTO``. The equivalent here is:: +of 0.5 and a normalizing flux of ``FLUX_AUTO``. The equivalent here is: + +.. code-block:: python sig = 2. / 2.35 * r # r from sep.flux_radius() above, with fluxfrac = 0.5 xwin, ywin, flag = sep.winpos(data, objs['x'], objs['y'], sig) @@ -164,25 +183,42 @@ of 0.5 and a normalizing flux of ``FLUX_AUTO``. The equivalent here is:: Segmentation-masked image measurements -------------------------------------- -SourceExtractor provides a mechanism for measuring the "AUTO" and "FLUX_RADIUS" parameters for a given object including a mask for neighboring sources. In addition to the mask, setting the SourceExtractor parameter ``MASK_TYPE=CORRECT`` further fills the masked pixels of a given source with "good" pixel values reflected opposite of the masked pixels. The ``SEP`` photometry and measurement functions provide an option for simple masking without reflection or subtracting neighbor flux. +SourceExtractor provides a mechanism for measuring the "AUTO" and +"FLUX_RADIUS" parameters for a given object including a mask for +neighboring sources. In addition to the mask, setting the SourceExtractor +parameter ``MASK_TYPE=CORRECT`` further fills the masked pixels of a given +source with "good" pixel values reflected opposite of the masked pixels. +The ``SEP`` photometry and measurement functions provide an option for +simple masking without reflection or subtracting neighbor flux. + +For example, using a segmentation array provided by ``sep.extract``, we +can compute the masked ``flux_radius`` that could otherwise be +artificially large due to flux from nearby sources: -For example, using a segmentation array provided by ``sep.extract``, we can compute the masked ``flux_radius`` that could otherwise be artificially large due to flux from nearby sources:: +.. code-block:: python # list of object id numbers that correspond to the segments seg_id = np.arange(1, len(objs)+1, dtype=np.int32) - r, flag = sep.flux_radius(data, objs['x'], objs['y'], 6.*objs['a'], 0.5, - seg_id=seg_id, seg=seg, + r, flag = sep.flux_radius(data, objs['x'], objs['y'], 6.*objs['a'], + 0.5, seg_id=seg_id, seg=seg, normflux=flux, subpix=5) -To enforce that a given measurement **only** includes pixels within a segment, provide negative values in the ``seg_id`` list. Otherwise the mask for a given object will be pixels with ``(seg == 0) | (seg_id == id_i)``. +To enforce that a given measurement **only** includes pixels within a +segment, provide negative values in the ``seg_id`` list. Otherwise the +mask for a given object will be pixels with +``(seg == 0) | (seg_id == id_i)``. -The following functions include the segmentation masking: ``sum_circle``, ``sum_circann``, ``sum_ellipse``, ``sum_ellipann``, ``flux_radius`` , and ``kron_radius`` (``winpos`` **currently does not**). +The following functions include the segmentation masking: +``sum_circle``, ``sum_circann``, ``sum_ellipse``, ``sum_ellipann``, +``flux_radius`` , and ``kron_radius`` (``winpos`` **currently does not**). Masking image regions --------------------- -Create a boolean array with elliptical regions set to True:: +Create a boolean array with elliptical regions set to True: + +.. code-block:: python mask = np.zeros(data.shape, dtype=np.bool) sep.mask_ellipse(mask, objs['x'], objs['y'], obs['a'], objs['b'], diff --git a/docs/conf.py b/docs/conf.py index aa98fef..1d50870 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,10 +15,9 @@ import os import sys +import sep_pjw import sphinx_rtd_theme -import sep - # generate api directory if it doesn't already exist if not os.path.exists("api"): os.mkdir("api") @@ -50,6 +49,7 @@ "numpydoc", "nbsphinx", "IPython.sphinxext.ipython_console_highlighting", + "sphinx_copybutton", ] numpydoc_show_class_members = False autosummary_generate = ["reference.rst"] @@ -69,18 +69,20 @@ master_doc = "index" # General information about the project. -project = "sep" -copyright = "2014-2016, Kyle Barbary and contributors" +project = "sep-pjw" +copyright = "2014-2024, Peter Watson and contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # THe short X.Y version. -version = ".".join(sep.__version__.split(".")[0:2]) +version = ".".join(sep_pjw.__version__.split(".")[0:2]) # The full version, including alpha/beta/rc tags. -release = sep.__version__ +release = sep_pjw.__version__ + +html_title = f"{project} {release.split('+')[0]} documentation" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -98,7 +100,7 @@ # The reST default role (used for this markup: `text`) to use for all # documents. -default_role = "obj" +default_role = "autolink" # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True @@ -125,8 +127,9 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +# html_theme = "sphinx_rtd_theme" +# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +html_theme = "furo" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -204,7 +207,7 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = "sepdoc" +htmlhelp_basename = "seppjwdoc" # -- Options for LaTeX output --------------------------------------------- diff --git a/docs/filter.rst b/docs/filter.rst index e8fece9..8eb473e 100644 --- a/docs/filter.rst +++ b/docs/filter.rst @@ -4,7 +4,11 @@ Matched Filter (Convolution) For source detection, SEP supports using a matched filter, which can give the optimal detection signal-to-noise for objects with some known shape. This is controlled using the ``filter_kernel`` keyword in -`sep.extract`. For example:: +`sep_pjw.extract`. For example: + +.. code-block:: python + + import sep_pjw as sep kernel = np.array([[1., 2., 3., 2., 1.], [2., 3., 5., 3., 2.], @@ -31,7 +35,7 @@ Correct treatment in the presence of variable noise In Source Extractor, the matched filter is implemented assuming there is equal noise across all pixels in the kernel. The matched filter then simplifies to a convolution of the data with the kernel. In -`sep.extract`, this is also the behavior when there is constant noise +`sep_pjw.extract`, this is also the behavior when there is constant noise (when ``err`` is not specified). In the presence of independent noise on each pixel, SEP uses a full @@ -40,7 +44,9 @@ each pixel. This is not available in Source Extractor. Some benefits of this method are that detector sensitivity can be taken into account and edge effects are handled gracefully. For example, suppose we have an image with noise that is higher in one region than another. This -can often occur when coadding images:: +can often occur when coadding images: + +.. code-block:: python # create a small image with higher noise in the upper left n = 16 @@ -81,10 +87,10 @@ the object: Derivation of the matched filter formula ---------------------------------------- -Assume that we have an image containing a single point source. This produces a -signal with PSF :math:`S_i` and noise :math:`N_i` at each pixel indexed by -:math:`i`. Then the measured image data :math:`D_i` (i.e. our pixel values) is -given by: +Assume that we have an image containing a single point source. This +produces a signal with PSF :math:`S_i` and noise :math:`N_i` at each pixel +indexed by :math:`i`. Then the measured image data :math:`D_i` (i.e. our +pixel values) is given by: .. math:: D_i = S_i + N_i @@ -95,9 +101,9 @@ output :math:`Y`: .. math:: Y = \sum_i T_i D_i = T^T D -We use matrix notation from here on and drop the explicit sums. Our objective -is to find the transformation :math:`T_i` which maximizes the signal-to-noise -ratio :math:`SNR`. +We use matrix notation from here on and drop the explicit sums. Our +objective is to find the transformation :math:`T_i` which maximizes the +signal-to-noise ratio :math:`SNR`. .. math:: SNR^2 = \frac{(T^T S)^2}{E[(T^T N)^2]} @@ -105,14 +111,16 @@ ratio :math:`SNR`. We can expand the denominator as: .. math:: - E[(T^T N)^2] = E[(T^T N)(N^T T)] = T^T \cdot E[N N^T] \cdot T = T^T C T + E[(T^T N)^2] = E[(T^T N)(N^T T)] = T^T \cdot E[N N^T] \cdot T + = T^T C T -Where :math:`C_{ik}` is the covariance of the noise between pixels :math:`i` -and :math:`k`. Now using the Cauchy-Schwarz inequality on the numerator: +Where :math:`C_{ik}` is the covariance of the noise between pixels +:math:`i` and :math:`k`. Now using the Cauchy-Schwarz inequality on the +numerator: .. math:: - (T^T S)^2 = (T^T C^{1/2} C^{-1/2} S)^2 \le (T^T C^{1/2})^2 (C^{-1/2} S)^2 = - (T^T C T) (S^T C^{-1} S) + (T^T S)^2 = (T^T C^{1/2} C^{-1/2} S)^2 \le (T^T C^{1/2})^2 + (C^{-1/2} S)^2 = (T^T C T) (S^T C^{-1} S) since :math:`C^T = C`. The signal-to-noise ratio is therefore bounded by: @@ -121,15 +129,16 @@ since :math:`C^T = C`. The signal-to-noise ratio is therefore bounded by: &SNR^2 \le S^T C^{-1} S Choosing :math:`T = \alpha C^{-1} S` where :math:`\alpha` is an arbitrary -normalization constant, we get equality. Hence this choise of :math:`T` is the -optimal linear tranformation. We normalize this linear transformation so that -if there is no signal and only noise, we get an expected signal-to-noise ratio -of 1. With this definition, the output :math:`SNR` represents the number of -standard deviations above the background. This gives: +normalization constant, we get equality. Hence this choise of :math:`T` is +the optimal linear tranformation. We normalize this linear transformation +so that if there is no signal and only noise, we get an expected +signal-to-noise ratio of 1. With this definition, the output :math:`SNR` +represents the number of standard deviations above the background. This +gives: .. math:: - &E[(T^T N)^2] = T^T C T = \alpha^2 S^T C^{-1} C C^{-1} S = \alpha^2 S^T - C^{-1} S = 1 \\ + &E[(T^T N)^2] = T^T C T = \alpha^2 S^T C^{-1} C C^{-1} S = \alpha^2 + S^T C^{-1} S = 1 \\ &\alpha = \frac{1}{\sqrt{S^T C^{-1} S}} Putting everything together, our normalized linear transformation is: @@ -137,7 +146,8 @@ Putting everything together, our normalized linear transformation is: .. math:: T = \frac{C^{-1} S}{\sqrt{S^T C^{-1} S}} -And the optimal signal-to-noise is given in terms of the known variables as: +And the optimal signal-to-noise is given in terms of the known variables +as: .. math:: SNR = \frac{S^T C^{-1} D}{\sqrt{S^T C^{-1} S}} diff --git a/docs/index.rst b/docs/index.rst index 7c8f2a2..c7d75a3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ -SEP -=== +SEP-PJW +======= *Python library for Source Extraction and Photometry* @@ -43,40 +43,52 @@ instructions. - Optimized matched filter for variable noise in source extraction. - Circular annulus and elliptical annulus aperture photometry functions. -- Local background subtraction in shape consistent with aperture in aperture - photometry functions. +- Local background subtraction in shape consistent with aperture in + aperture photometry functions. - Exact pixel overlap mode in all aperture photometry functions. - Masking of elliptical regions on images. +SEP-PJW vs. SEP +............... + +The original release of ``sep`` by Kyle Barbary, +`kbarbary/sep `_, no longer appears to be +maintained. Whilst the package is incredibly useful, there are a few +outstanding bugs, and support for the latest versions of `Python` will be +limited. The aim of ``sep-pjw`` is to offer a version of sep that resolves +these issues, whilst maintaining compatibility as much as is feasibly +possible. + + Installation ------------ with conda .......... -SEP can be installed with conda from the ``conda-forge`` channel:: +SEP-PJW can be installed with conda from the ``conda-forge`` channel:: - conda install -c conda-forge sep + conda install -c conda-forge sep-pjw with pip ........ -SEP can also be installed with `pip `_. After ensuring -that numpy is installed, run :: +SEP-PJW can also be installed with `pip `_. After +ensuring that numpy is installed, run :: - pip install sep + python -m pip install sep-pjw If you get an error about permissions, you are probably using your system Python. In this case, I recommend using `pip's "user install" `_ option to install sep into your user directory :: - pip install --user sep + python -m pip install --user sep-pjw -Do **not** install sep or other third-party Python packages using ``sudo`` -unless you are fully aware of the risks. +Do **not** install ``sep-pjw`` or other third-party Python packages using +``sudo`` unless you are fully aware of the risks. Usage Guide @@ -100,10 +112,11 @@ For complete API documentation, see :doc:`reference`. Contributing ------------ -Report a bug or documentation issue: http://github.com/kbarbary/sep/issues +Report a bug or documentation issue: +http://github.com/PJ-Watson/sep-pjw/issues -Development of SEP takes place on GitHub at -http://github.com/kbarbary/sep. Contributions of bug fixes, +Development of ``sep-pjw`` takes place on GitHub at +http://github.com/PJ-Watson/sep-pjw. Contributions of bug fixes, documentation improvements and minor feature additions are welcome via GitHub pull requests. For major features, it is best to open an issue discussing the change first. diff --git a/docs/reference.rst b/docs/reference.rst index 07a114b..833f8f7 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -7,54 +7,54 @@ Reference/API .. autosummary:: :toctree: api - sep.Background - sep.extract + sep_pjw.Background + sep_pjw.extract **Aperture photometry** .. autosummary:: :toctree: api - sep.sum_circle - sep.sum_circann - sep.sum_ellipse - sep.sum_ellipann + sep_pjw.sum_circle + sep_pjw.sum_circann + sep_pjw.sum_ellipse + sep_pjw.sum_ellipann **Aperture utilities** .. autosummary:: :toctree: api - sep.kron_radius - sep.flux_radius - sep.winpos - sep.mask_ellipse - sep.ellipse_axes - sep.ellipse_coeffs + sep_pjw.kron_radius + sep_pjw.flux_radius + sep_pjw.winpos + sep_pjw.mask_ellipse + sep_pjw.ellipse_axes + sep_pjw.ellipse_coeffs **Low-level utilities** .. autosummary:: :toctree: api - sep.get_extract_pixstack - sep.set_extract_pixstack - sep.get_sub_object_limit - sep.set_sub_object_limit + sep_pjw.get_extract_pixstack + sep_pjw.set_extract_pixstack + sep_pjw.get_sub_object_limit + sep_pjw.set_sub_object_limit **Flags** -======================== =========================================== -Flag Description -======================== =========================================== -``sep.OBJ_MERGED`` object is result of deblending -``sep.OBJ_TRUNC`` object is truncated at image boundary -``sep.OBJ_SINGU`` x, y fully correlated in object -``sep.APER_TRUNC`` aperture truncated at image boundary -``sep.APER_HASMASKED`` aperture contains one or more masked pixels -``sep.APER_ALLMASKED`` aperture contains only masked pixels -``sep.APER_NONPOSITIVE`` aperture sum is negative in ``kron_radius`` -======================== =========================================== +============================ =========================================== +Flag Description +============================ =========================================== +``sep_pjw.OBJ_MERGED`` object is result of deblending +``sep_pjw.OBJ_TRUNC`` object is truncated at image boundary +``sep_pjw.OBJ_SINGU`` x, y fully correlated in object +``sep_pjw.APER_TRUNC`` aperture truncated at image boundary +``sep_pjw.APER_HASMASKED`` aperture contains one or more masked pixels +``sep_pjw.APER_ALLMASKED`` aperture contains only masked pixels +``sep_pjw.APER_NONPOSITIVE`` aperture sum is negative in ``kron_radius`` +============================ =========================================== To see if a given flag is set in ``flags``:: diff --git a/docs/tutorial.ipynb b/docs/tutorial.ipynb index fcf239f..3ef4af8 100644 --- a/docs/tutorial.ipynb +++ b/docs/tutorial.ipynb @@ -20,7 +20,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "import sep" + "import sep_pjw as sep" ] }, { @@ -366,8 +366,11 @@ "references to ``data``, as they will be rendered nonsensical.\n", "\n", "For the interested reader, this byteswap operation is necessary because\n", - "astropy.io.fits always returns big-endian byte order arrays, even on\n", - "little-endian machines. For more on this, see\n", + "``astropy.io.fits`` always returns big-endian byte order arrays, even on\n", + "little-endian machines. This is due to the FITS standard requiring\n", + "big-endian arrays (see the \n", + "[FITS Standard](https://fits.gsfc.nasa.gov/fits_standard.html)), and \n", + "``astropy.io.fits`` aiming for backwards compatibility.\n", "" ] } From 3eee142d6483f6e00b0be6954df720fdab3ace04 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:03:13 +0100 Subject: [PATCH 16/47] Rename and rebuild docs. --- .readthedocs.yaml | 39 +++++++++++++++++++++++++++++++++++++++ README.md | 2 +- pyproject.toml | 8 +++++--- 3 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..773d60b --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,39 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # You can also specify other tool versions: + # nodejs: "20" + # rust: "1.70" + # golang: "1.20" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs + # builder: "dirhtml" + # Fail on all warnings to avoid broken references + # fail_on_warning: true + +# Optionally build your docs in additional formats such as PDF and ePub +# formats: +# - pdf +# - epub + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + # - requirements: docs/requirements.txt + - method: pip + path: . + extra_requirements: + - docs diff --git a/README.md b/README.md index aae5fa8..16feb06 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ instructions. Python ------ -**Documentation:** http://sep.readthedocs.io +**Documentation:** http://sep-pjw.readthedocs.io **Requirements:** diff --git a/pyproject.toml b/pyproject.toml index c53aaf7..f9a52c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,16 +42,18 @@ docs = [ "sphinx-copybutton>=0.5", "sphinx>=7.2", "numpydoc>=1.6", + "matplotlib>=3.6", "furo>=2024.1", "nbsphinx>=0.9.3", "pandoc>=2.3", "fitsio>=1.2.1", + "jupyter", ] [project.urls] -"Homepage" = "https://github.com/PJ-Watson/sep" -"Bug Tracker" = "https://github.com/PJ-Watson/sep/issues" -"(Old) Documentation" = "http://sep.readthedocs.org" +"Homepage" = "https://github.com/PJ-Watson/sep-pjw" +"Bug Tracker" = "https://github.com/PJ-Watson/sep-pjw/issues" +"(Old) Documentation" = "http://sep-pjw.readthedocs.org" [tool.setuptools_scm] version_file = "src/_version.py" From ab0282320281668756b79de7c5c91f74aeac5ede Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Sat, 3 Feb 2024 17:08:35 +0100 Subject: [PATCH 17/47] Fix missing import. --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 1d50870..2cf77f4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,6 @@ import sys import sep_pjw -import sphinx_rtd_theme # generate api directory if it doesn't already exist if not os.path.exists("api"): From 522a364d010934e27ec701b725624f21f7c78543 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Sun, 4 Feb 2024 13:57:49 +0100 Subject: [PATCH 18/47] Update readme, fix tests. --- .../workflows/build-wheels-upload-pypi.yml | 131 +++++++----------- .github/workflows/python-package-tox.yml | 12 +- README.md | 57 ++++---- test.py | 2 +- 4 files changed, 89 insertions(+), 113 deletions(-) diff --git a/.github/workflows/build-wheels-upload-pypi.yml b/.github/workflows/build-wheels-upload-pypi.yml index 5b3d2ef..a2c349f 100644 --- a/.github/workflows/build-wheels-upload-pypi.yml +++ b/.github/workflows/build-wheels-upload-pypi.yml @@ -3,11 +3,11 @@ name: build-wheels-upload-pypi -on: - push: - # Run this action on the trigger event when *any* tag is pushed - tags: - - '*' +on: [push] + # push: + # # Run this action on the trigger event when *any* tag is pushed + # tags: + # - '*' jobs: @@ -27,21 +27,21 @@ jobs: steps: - name: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: install_python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.12' # For the build, sep needs numpy and cython and we add twine and wheel # for better testing and checking. - name: Install dependencies - run: python -m pip install twine numpy wheel cython + run: python -m pip install setuptools twine numpy wheel cython build - name: Build sdist - run: python setup.py sdist + run: python -m build --sdist - name: Show files run: ls -lh dist @@ -51,7 +51,7 @@ jobs: run: twine check dist/* - name: Upload sdist - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: dist path: dist/*.tar.gz @@ -62,98 +62,65 @@ jobs: # done with the help of the `cibuildwheel` package. # # The wheels are built for Windows, Linux and MacOS and the python versions - # 3.5 - 3.10. + # 3.9 - 3.12. # # The three operating system could be done in parallel. - + name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} - env: - CIBW_ARCHS_MACOS: "x86_64 universal2 arm64" - strategy: - max-parallel: 4 matrix: - python-version: [3.9] + python-version: ["3.9", "3.10", "3.11", "3.12"] os: [windows-latest, macos-latest, ubuntu-latest] include: - os: ubuntu-20.04 cibw_archs: "aarch64" steps: - - name: setup QEMU - if: matrix.cibw_archs == 'aarch64' - uses: docker/setup-qemu-action@v1 - with: - platforms: arm64 - - - name: checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + - name: Set up QEMU + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@v3 with: - python-version: ${{ matrix.python-version }} - - - name: Install cibuildwheel - run: python -m pip install cibuildwheel - - - name: Build wheels - if: matrix.cibw_archs == 'aarch64' - run: python -m cibuildwheel --output-dir wheelhouse - env: - CIBW_BUILD: "cp3*" - CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 - CIBW_MANYLINUX_I686_IMAGE: manylinux2014 - CIBW_BUILD_VERBOSITY: 1 - CIBW_SKIP: '*-musllinux_*' - CIBW_ARCHS_LINUX: "aarch64" + platforms: all - name: Build wheels - if: matrix.cibw_archs != 'aarch64' - run: python -m cibuildwheel --output-dir wheelhouse + uses: pypa/cibuildwheel@v2.16.5 env: - CIBW_BUILD: "cp3*" - CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 - CIBW_MANYLINUX_I686_IMAGE: manylinux2014 - CIBW_BUILD_VERBOSITY: 1 - CIBW_SKIP: '*-musllinux_*' - CIBW_ARCHS_LINUX: "auto" - - - name: Show files - run: ls -lh wheelhouse - shell: bash + # configure cibuildwheel to build native archs ('auto'), and some + # emulated ones + CIBW_ARCHS_LINUX: auto aarch64 ppc64le s390x - - name: Upload wheels - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: - name: dist + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: ./wheelhouse/*.whl - upload_to_pypi: + # upload_to_pypi: - # Finally we collect all out data from the artifacts and put them back to - # dist directory for upload. The final step waits for the other jobs to be - # finished and starts only if the trigger event of the action was a push - # of a tag starting with as version separation. All other jobs run - # without heading + # # Finally we collect all out data from the artifacts and put them back to + # # dist directory for upload. The final step waits for the other jobs to be + # # finished and starts only if the trigger event of the action was a push + # # of a tag starting with as version separation. All other jobs run + # # without heading - runs-on: [ubuntu-latest] - needs: [build_wheels, build_sdist] + # runs-on: [ubuntu-latest] + # needs: [build_wheels, build_sdist] - # upload to PyPI on every tag starting with 'v' - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') + # # upload to PyPI on every tag starting with 'v' + # if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') - steps: - - uses: actions/setup-python@v2 - - - uses: actions/download-artifact@v2 - with: - name: dist - path: dist - - - name: upload_to_pypi - uses: pypa/gh-action-pypi-publish@v1.4.1 - with: - user: __token__ - password: ${{ secrets.PYPI_TOKEN }} - skip_existing: true + # steps: + # - uses: actions/setup-python@v2 + + # - uses: actions/download-artifact@v2 + # with: + # name: dist + # path: dist + + # - name: upload_to_pypi + # uses: pypa/gh-action-pypi-publish@v1.4.1 + # with: + # user: __token__ + # password: ${{ secrets.PYPI_TOKEN }} + # skip_existing: true diff --git a/.github/workflows/python-package-tox.yml b/.github/workflows/python-package-tox.yml index c034256..eb4b0f3 100644 --- a/.github/workflows/python-package-tox.yml +++ b/.github/workflows/python-package-tox.yml @@ -5,9 +5,9 @@ name: Python package on: push: - branches: [ master ] + branches: [ main ] pull_request: - branches: [ master ] + branches: [ main ] jobs: build: @@ -15,16 +15,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: [3.5, 3.8] + python: ["3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install Tox and any other packages - run: pip install tox + run: python -m pip install tox - name: Run Tox # Run tox using the version of Python in `PATH` run: tox -e py diff --git a/README.md b/README.md index 16feb06..15e1b97 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,33 @@ -IMPORTANT ---------- - -This repository (all versions >= 1.3) was forked from https://github.com/kbarbary/sep in November 2023, as the original library no longer appears to be maintained. The current version can be installed by cloning this repository, and running -``` -python -m pip install . -``` -Changes are described in [CHANGES.md](./CHANGES.md), with all other documentation remaining unaltered. +SEP-PJW +======= -SEP -=== - -Python and C library for Source Extraction and Photometry +Python and C library for Source Extraction and Photometry, forked from [kbarbary/sep](https://github.com/kbarbary/sep) to provide additional features and bug fixes. ![Build Status](https://github.com/kbarbary/sep/workflows/Python%20package/badge.svg) [![PyPI](https://img.shields.io/pypi/v/sep.svg)](https://pypi.python.org/pypi/sep) +[![Documentation Status](https://readthedocs.org/projects/sep-pjw/badge/?version=latest)](https://sep-pjw.readthedocs.io/en/latest/?badge=latest) [![JOSS](http://joss.theoj.org/papers/10.21105/joss.00058/status.svg)](http://dx.doi.org/10.21105/joss.00058) *"... [it's] an SEP: Somebody Else's Problem." "Oh, good. I can relax then."* +SEP-PJW vs. SEP +--------------- + +The original release of `sep` by Kyle Barbary, +[kbarbary/sep](https://github.com/kbarbary/sep), no longer appears to be +maintained. Whilst the package is incredibly useful, there are a few +outstanding bugs, and support for the latest versions of Python +(`python>=3.11`) will be limited. The aim of `sep-pjw` is to offer a +version of `sep` that resolves these issues, whilst maintaining +compatibility as much as is feasibly possible. Any fixes or updates to +the original library are documented in [CHANGES.md](./CHANGES.md). For +existing workflows, the only necessary update will be to change the import +to +```python +import sep_pjw as sep +``` + About ----- @@ -56,24 +65,24 @@ Python **Requirements:** -- Python 3.5+ -- numpy +- Python 3.9+ +- numpy 1.23+ **Install release version:** -SEP can be installed with [pip](https://pip.pypa.io): +SEP-PJW can be installed with [pip](https://pip.pypa.io): ``` -pip install sep +python -m pip install sep-pjw ``` If you get an error about permissions, you are probably using your system Python. In this case, we recommend using [pip's "user install"](https://pip.pypa.io/en/latest/user_guide/#user-installs) -option to install sep into your user directory: +option to install sep-pjw into your user directory: ``` -pip install --user sep +python -m pip install --user sep-pjw ``` Do **not** install sep or other third-party Python packages using @@ -142,14 +151,14 @@ and header file in `/path/to/prefix/include`. The default prefix is Contributing ------------ -- Report a bug or documentation issue: http://github.com/kbarbary/sep/issues -- Ask (or answer) a question: https://github.com/kbarbary/sep/discussions/categories/q-a +- Report a bug or documentation issue: http://github.com/PJ-Watson/sep-pjw/issues +- Ask (or answer) a question: https://github.com/PJ-Watson/sep-pjw/discussions/categories/q-a -Development of SEP takes place on GitHub at -http://github.com/kbarbary/sep. Contributions of bug fixes, +Development of SEP-PJW takes place on GitHub at +[PJ-Watson/sep-pjw](http://github.com/PJ-Watson/sep-pjw). Contributions of bug fixes, documentation improvements and minor feature additions are welcome via GitHub pull requests. For major features, it is best to discuss the change first -via [GitHub Discussions](https://github.com/kbarbary/sep/discussions/). +via [GitHub Discussions](https://github.com/PJ-Watson/sep-pjw/discussions/). Citation @@ -269,4 +278,4 @@ the data. **I have more questions!** -Open a discussion on the [GitHub Discussions page](https://github.com/kbarbary/sep/discussions/categories/q-a)! +Open a discussion on the [GitHub Discussions page](https://github.com/PJ-Watson/sep-pjw/discussions/categories/q-a)! diff --git a/test.py b/test.py index dea4079..437e6ae 100755 --- a/test.py +++ b/test.py @@ -7,7 +7,7 @@ import pytest from numpy.testing import assert_allclose, assert_approx_equal, assert_equal -import sep +import sep_pjw as sep # unicode_literals doesn't play well with numpy dtype field names From 110e49d99091ba6e4373ed74960651d555f11be1 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:36:49 +0100 Subject: [PATCH 19/47] Fix int64 imports on windows. --- src/analyse.c | 3 ++- src/aperture.c | 1 + src/background.c | 1 + src/convolve.c | 3 ++- src/deblend.c | 1 + src/lutz.c | 1 + src/util.c | 3 ++- 7 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/analyse.c b/src/analyse.c index fdf2258..4b08989 100644 --- a/src/analyse.c +++ b/src/analyse.c @@ -21,8 +21,9 @@ *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include -#include +#include #include +#include #include #include "sep.h" #include "sepcore.h" diff --git a/src/aperture.c b/src/aperture.c index ca194ad..c11e01c 100644 --- a/src/aperture.c +++ b/src/aperture.c @@ -21,6 +21,7 @@ *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include +#include #include #include #include diff --git a/src/background.c b/src/background.c index 64128c1..cee2db0 100644 --- a/src/background.c +++ b/src/background.c @@ -21,6 +21,7 @@ *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include +#include #include #include #include diff --git a/src/convolve.c b/src/convolve.c index 8f4701e..5419fcd 100644 --- a/src/convolve.c +++ b/src/convolve.c @@ -20,8 +20,9 @@ f* GNU Lesser General Public License for more details. * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#include +#include #include +#include #include #include #include "sep.h" diff --git a/src/deblend.c b/src/deblend.c index 279e722..3cf0843 100644 --- a/src/deblend.c +++ b/src/deblend.c @@ -21,6 +21,7 @@ *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include +#include #include #include #include diff --git a/src/lutz.c b/src/lutz.c index 93bd763..2a11017 100644 --- a/src/lutz.c +++ b/src/lutz.c @@ -23,6 +23,7 @@ /* Note: was extract.c in SExtractor. */ #include +#include #include #include #include diff --git a/src/util.c b/src/util.c index ff3f72d..f66daf0 100644 --- a/src/util.c +++ b/src/util.c @@ -16,6 +16,7 @@ *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include +#include #include #include #include @@ -24,7 +25,7 @@ #define DETAILSIZE 512 -const char *const sep_version_string = "1.3.1.dev"; +const char *const sep_version_string = "1.3.1.1"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ From c486d97888cafe610ae3240b6352d2df0aa3bf15 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:39:20 +0100 Subject: [PATCH 20/47] Fix VLA not compiling on windows builds. --- src/extract.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/extract.c b/src/extract.c index 545dffa..3166335 100644 --- a/src/extract.c +++ b/src/extract.c @@ -221,8 +221,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, sum = 0.0; w = image->w; h = image->h; + numids = (image->numids) ? image->numids : 1; - infostruct idinfo[numids]; + infostruct *idinfo; + QCALLOC(idinfo, infostruct, numids, status); + prevpix = 0; isvarthresh = 0; relthresh = 0.0; @@ -763,6 +766,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, freedeblend(&deblendctx); free(pixel); free(info); + free(idinfo); free(store); free(marker); free(dummyscan); From c62175411f65764331da7dd00b8e6f538c5524a9 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:49:25 +0100 Subject: [PATCH 21/47] Correct manifest, fix wheels. --- .../workflows/build-wheels-upload-pypi.yml | 111 ++++++++++++------ MANIFEST.in | 4 +- 2 files changed, 80 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build-wheels-upload-pypi.yml b/.github/workflows/build-wheels-upload-pypi.yml index a2c349f..c623e8b 100644 --- a/.github/workflows/build-wheels-upload-pypi.yml +++ b/.github/workflows/build-wheels-upload-pypi.yml @@ -3,7 +3,7 @@ name: build-wheels-upload-pypi -on: [push] +on: [push, workflow_dispatch] # push: # # Run this action on the trigger event when *any* tag is pushed # tags: @@ -28,6 +28,9 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true - name: install_python uses: actions/setup-python@v5 @@ -67,9 +70,13 @@ jobs: # The three operating system could be done in parallel. name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} + env: + CIBW_ARCHS_MACOS: "x86_64 universal2 arm64" + strategy: + max-parallel: 4 matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.12"] os: [windows-latest, macos-latest, ubuntu-latest] include: - os: ubuntu-20.04 @@ -82,45 +89,83 @@ jobs: if: runner.os == 'Linux' uses: docker/setup-qemu-action@v3 with: - platforms: all + platforms: arm64 + + - name: checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install cibuildwheel + run: python -m pip install cibuildwheel - name: Build wheels - uses: pypa/cibuildwheel@v2.16.5 + if: matrix.cibw_archs == 'aarch64' + run: python -m cibuildwheel --output-dir wheelhouse env: - # configure cibuildwheel to build native archs ('auto'), and some - # emulated ones - CIBW_ARCHS_LINUX: auto aarch64 ppc64le s390x + CIBW_BUILD: "cp39-*" #" cp310-* cp311-* cp312-*" + CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_MANYLINUX_I686_IMAGE: manylinux2014 + CIBW_BUILD_VERBOSITY: 1 + CIBW_SKIP: '*-musllinux_*' + CIBW_ARCHS_LINUX: "aarch64" - - uses: actions/upload-artifact@v4 - with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl + - name: Build wheels + if: matrix.cibw_archs != 'aarch64' + run: python -m cibuildwheel --output-dir wheelhouse + env: + CIBW_BUILD: "cp39-* " #" cp310-* cp311-* cp312-*" + CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_MANYLINUX_I686_IMAGE: manylinux2014 + CIBW_BUILD_VERBOSITY: 1 + CIBW_SKIP: '*-musllinux_*' + CIBW_ARCHS_LINUX: "auto" - # upload_to_pypi: + - name: Show files + run: ls -lh wheelhouse + shell: bash - # # Finally we collect all out data from the artifacts and put them back to - # # dist directory for upload. The final step waits for the other jobs to be - # # finished and starts only if the trigger event of the action was a push - # # of a tag starting with as version separation. All other jobs run - # # without heading + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: dist-${{ matrix.os }} + path: ./wheelhouse/*.whl - # runs-on: [ubuntu-latest] - # needs: [build_wheels, build_sdist] + upload_to_pypi: - # # upload to PyPI on every tag starting with 'v' - # if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') + # Finally we collect all out data from the artifacts and put them back to + # dist directory for upload. The final step waits for the other jobs to be + # finished and starts only if the trigger event of the action was a push + # of a tag starting with as version separation. All other jobs run + # without heading - # steps: - # - uses: actions/setup-python@v2 + runs-on: [ubuntu-latest] + needs: [build_wheels, build_sdist] + environment: + name: testpypi + url: https://test.pypi.org/p/sep-pjw + permissions: + id-token: write - # - uses: actions/download-artifact@v2 - # with: - # name: dist - # path: dist + # upload to PyPI on every tag starting with 'v' + if: github.event_name == 'push' # && startsWith(github.event.ref, 'refs/tags/v') - # - name: upload_to_pypi - # uses: pypa/gh-action-pypi-publish@v1.4.1 - # with: - # user: __token__ - # password: ${{ secrets.PYPI_TOKEN }} - # skip_existing: true + steps: + - uses: actions/setup-python@v5 + + - uses: actions/download-artifact@v4 + with: + pattern: dist* + merge-multiple: true + path: dist + + - name: upload_to_pypi + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ diff --git a/MANIFEST.in b/MANIFEST.in index 178f13c..52cf06e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,5 +5,5 @@ include licenses/* include pyproject.toml include src/*.[c,h,i] -include sep.pyx -exclude sep.c +include sep_pjw.pyx +exclude sep_pjw.c From 9d64024e1b76b1e79f5b64a5a79cfd9d1c1a6c04 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:59:26 +0100 Subject: [PATCH 22/47] Publish to PyPI, update version. --- .github/workflows/build-wheels-upload-pypi.yml | 14 +++++++------- src/util.c | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-wheels-upload-pypi.yml b/.github/workflows/build-wheels-upload-pypi.yml index c623e8b..e8d39ec 100644 --- a/.github/workflows/build-wheels-upload-pypi.yml +++ b/.github/workflows/build-wheels-upload-pypi.yml @@ -109,7 +109,7 @@ jobs: if: matrix.cibw_archs == 'aarch64' run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: "cp39-*" #" cp310-* cp311-* cp312-*" + CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-*" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_MANYLINUX_I686_IMAGE: manylinux2014 CIBW_BUILD_VERBOSITY: 1 @@ -120,7 +120,7 @@ jobs: if: matrix.cibw_archs != 'aarch64' run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: "cp39-* " #" cp310-* cp311-* cp312-*" + CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-*" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_MANYLINUX_I686_IMAGE: manylinux2014 CIBW_BUILD_VERBOSITY: 1 @@ -148,13 +148,13 @@ jobs: runs-on: [ubuntu-latest] needs: [build_wheels, build_sdist] environment: - name: testpypi - url: https://test.pypi.org/p/sep-pjw + name: pypi + url: https://pypi.org/p/sep-pjw permissions: id-token: write # upload to PyPI on every tag starting with 'v' - if: github.event_name == 'push' # && startsWith(github.event.ref, 'refs/tags/v') + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v') steps: - uses: actions/setup-python@v5 @@ -167,5 +167,5 @@ jobs: - name: upload_to_pypi uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://test.pypi.org/legacy/ + # with: + # repository-url: https://test.pypi.org/legacy/ diff --git a/src/util.c b/src/util.c index f66daf0..f387e82 100644 --- a/src/util.c +++ b/src/util.c @@ -25,7 +25,7 @@ #define DETAILSIZE 512 -const char *const sep_version_string = "1.3.1.1"; +const char *const sep_version_string = "1.3.2"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ From e64968be33f2aa3b99e4308f901fbca6e8cb70cf Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:31:52 +0100 Subject: [PATCH 23/47] Small typos, fix some C compilation warnings. --- bench.py | 3 +-- pyproject.toml | 2 +- sep_pjw.pyx | 12 +++++++++--- setup.py | 5 ++++- src/deblend.c | 1 + src/extract.c | 16 +++++++--------- test.py | 3 +-- 7 files changed, 24 insertions(+), 18 deletions(-) diff --git a/bench.py b/bench.py index 80e0993..caa2448 100755 --- a/bench.py +++ b/bench.py @@ -5,8 +5,7 @@ from os.path import join import numpy as np - -import sep +import sep_pjw as sep # try to import photutils for comparison timing try: diff --git a/pyproject.toml b/pyproject.toml index f9a52c0..733e976 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,7 @@ docs = [ [project.urls] "Homepage" = "https://github.com/PJ-Watson/sep-pjw" "Bug Tracker" = "https://github.com/PJ-Watson/sep-pjw/issues" -"(Old) Documentation" = "http://sep-pjw.readthedocs.org" +"Documentation" = "http://sep-pjw.readthedocs.org" [tool.setuptools_scm] version_file = "src/_version.py" diff --git a/sep_pjw.pyx b/sep_pjw.pyx index 2482b31..c8ca2b6 100644 --- a/sep_pjw.pyx +++ b/sep_pjw.pyx @@ -658,10 +658,16 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, Perform cleaning? Default is True. clean_param : float, optional Cleaning parameter (see SExtractor manual). Default is 1.0. - segmentation_map : bool, optional - If True, also return a "segmentation map" giving the member + segmentation_map : `~numpy.ndarray` or bool, optional + If ``True``, also return a "segmentation map" giving the member pixels of each object. Default is False. + *New in v1.3.0*: + An existing segmentation map can also be supplied in + the form of an `~numpy.ndarray`. If this is the case, then the + object detection stage is skipped, and the objects in the + segmentation map are analysed and extracted. + Returns ------- objects : `~numpy.ndarray` @@ -692,7 +698,7 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, Array of integers with same shape as data. Pixels not belonging to any object have value 0. All pixels belonging to the ``i``-th object (e.g., ``objects[i]``) have value ``i+1``. Only returned if - ``segmentation_map=True``. + ``segmentation_map = True | ~numpy.ndarray``. """ cdef int kernelw, kernelh, status, i, j diff --git a/setup.py b/setup.py index 67543cb..ffe45e9 100755 --- a/setup.py +++ b/setup.py @@ -35,7 +35,10 @@ sourcefiles, include_dirs=include_dirs, depends=headerfiles, - define_macros=[("_USE_MATH_DEFINES", "1")], + define_macros=[ + ("_USE_MATH_DEFINES", "1"), + ("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"), + ], ) ] extensions = cythonize(extensions) diff --git a/src/deblend.c b/src/deblend.c index 3cf0843..24b9339 100644 --- a/src/deblend.c +++ b/src/deblend.c @@ -74,6 +74,7 @@ int deblend(objliststruct *objlistin, objliststruct *objlistout, submap = NULL; status = RETURN_OK; xn = deblend_nthresh; + l = 0; /* reset global static objlist for deblending */ objliststruct *const objlist = ctx->objlist; diff --git a/src/extract.c b/src/extract.c index 3166335..be12a5c 100644 --- a/src/extract.c +++ b/src/extract.c @@ -180,7 +180,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, size_t mem_pixstack; int64_t nposize, oldnposize; int64_t w, h; - int64_t co, i, j, pstop, xl, xl2, yl, cn; + int64_t co, i, pstop, xl, xl2, yl, cn; int64_t ididx, numids, totnpix; int64_t prevpix, bufh; int64_t stacksize, convn; @@ -190,7 +190,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, float sum; pixstatus cs, ps; - infostruct *info, *store; + infostruct *info, *store, *idinfo; objliststruct *finalobjlist; pliststruct *pixel, *pixt; char *marker; @@ -211,9 +211,10 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, sigscan = workscan = NULL; info = NULL; store = NULL; + idinfo = NULL; marker = NULL; psstack = NULL; - start = end = NULL; + start = end = cumcounts = NULL; finalobjlist = NULL; survives = NULL; cat = NULL; @@ -223,7 +224,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, h = image->h; numids = (image->numids) ? image->numids : 1; - infostruct *idinfo; + QCALLOC(cumcounts, int64_t, numids, status); QCALLOC(idinfo, infostruct, numids, status); prevpix = 0; @@ -237,13 +238,12 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, mem_pixstack = sep_get_extract_pixstack(); if (image->segmap) { - QCALLOC(cumcounts, int64_t, numids, status) totnpix = 0; for (i=0; iidcounts[i]; } - if (totnpix>mem_pixstack) { + if (sizeof(totnpix)>mem_pixstack) { goto exit; } mem_pixstack = totnpix + 1; @@ -760,13 +760,13 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, free(finalobjlist); } if (image->segmap) { - free(cumcounts); arraybuffer_free(&sbuf); } freedeblend(&deblendctx); free(pixel); free(info); free(idinfo); + free(cumcounts); free(store); free(marker); free(dummyscan); @@ -802,9 +802,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, int segsortit(infostruct *info, objliststruct *objlist, objliststruct *finalobjlist, double gain) { - objliststruct objlistout, *objlist2; objstruct obj; - int64_t i; int status; status = RETURN_OK; diff --git a/test.py b/test.py index 437e6ae..7cf6f24 100755 --- a/test.py +++ b/test.py @@ -5,9 +5,8 @@ import numpy as np import pytest -from numpy.testing import assert_allclose, assert_approx_equal, assert_equal - import sep_pjw as sep +from numpy.testing import assert_allclose, assert_approx_equal, assert_equal # unicode_literals doesn't play well with numpy dtype field names From 72477c2ffe0f5684fa62b9c3ff77a3ed9af6386e Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:49:38 +0100 Subject: [PATCH 24/47] Updates to readme, documenting changes. --- CHANGES.md | 44 +++++++++++++++++++++++++++++++++----------- README.md | 4 ++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index aade567..02d85ce 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,17 +1,39 @@ -v1.3.0 (1 December 2023) -==================== +v1.3.2 (5 February 2023) +======================== -* The `segmentation_map` argument of `sep.extract()` will now accept either an - array or boolean. If an existing segmentation map is passed, the object - detection stage is skipped, and sources will be individually analysed - according to the provided map. This change is backwards-compatible with - respect to the Python module. +* Move documentation to new location, fix package names and imports. +* Add wheels for Python 3.11/3.12. +* Fix C compilation errors on windows (VLAs). +* Publish updated version to PyPI under new name. - Please note that as no deblending is performed, - the calculated thresholds (and any dependent parameters) may not be the same - as originally derived. +v1.3.1 (31 January 2023) +======================== -* Use 64-bit integers throughout, to fix memory addressing with large arrays ([#122](https://github.com/kbarbary/sep/issues/122 "Original issue"), inspired by [Gabe Brammer's fork](https://github.com/gbrammer/sep) with additional fixes). +* Formatting changes (follow [black](https://github.com/psf/black) + formatting style). +* Fix `bench.py` and `test.py`, removing deprecated functions. +* Move metadata into `pyproject.toml`. +* Add pre-commit hooks for code and docstring validation. +* Change to dynamic versioning (git tag/commit based). + +v1.3.0 (1 December 2023) +======================== + +* The `segmentation_map` argument of `sep.extract()` will now accept + either an array or boolean. If an existing segmentation map is passed, + the object detection stage is skipped, and sources will be individually + analysed according to the provided map. This change is + backwards-compatible with respect to the Python module. + + Please note that as no deblending is performed, the calculated + thresholds (and any dependent parameters) may not be the same as + originally derived. + +* Use 64-bit integers throughout, to fix memory addressing with large + arrays + ([#122](https://github.com/kbarbary/sep/issues/122 "Original issue"), + inspired by [Gabe Brammer's fork](https://github.com/gbrammer/sep) + with additional fixes). v1.2.1 (1 June 2022) diff --git a/README.md b/README.md index 15e1b97..0d3b18d 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ SEP-PJW Python and C library for Source Extraction and Photometry, forked from [kbarbary/sep](https://github.com/kbarbary/sep) to provide additional features and bug fixes. -![Build Status](https://github.com/kbarbary/sep/workflows/Python%20package/badge.svg) -[![PyPI](https://img.shields.io/pypi/v/sep.svg)](https://pypi.python.org/pypi/sep) +![Build Status](https://github.com/PJ-Watson/sep-pjw/workflows/Python%20package/badge.svg) +[![PyPI](https://img.shields.io/pypi/v/sep-pjw.svg)](https://pypi.python.org/pypi/sep-pjw) [![Documentation Status](https://readthedocs.org/projects/sep-pjw/badge/?version=latest)](https://sep-pjw.readthedocs.io/en/latest/?badge=latest) [![JOSS](http://joss.theoj.org/papers/10.21105/joss.00058/status.svg)](http://dx.doi.org/10.21105/joss.00058) From 690bac795be85ec730f1635ff414e496f33f9fab Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Tue, 6 Feb 2024 12:28:11 +0100 Subject: [PATCH 25/47] Fix precision loss in threshold calculation, minor compilation errors, and reinstate all tests. --- pyproject.toml | 4 ++-- setup.py | 5 ++++- src/aperture.c | 4 ++-- src/background.c | 2 +- src/extract.c | 7 ++++--- test.py | 4 ++-- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 733e976..8f93fe1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,10 +17,10 @@ authors = [ { name="Michael Wuertenberger"}, { name="Ingvar Stepanyan"}, { name="Gabe Brammer"}, - { name="Peter Watson", email="peter.watson+pyGFE@inaf.it" }, + { name="Peter Watson", email="peter.watson+sep@inaf.it" }, ] maintainers = [ - { name="Peter Watson", email="peter.watson+pyGFE@inaf.it" }, + { name="Peter Watson", email="peter.watson+sep@inaf.it" }, ] description="Astronomical source extraction and photometry library" readme = "README.md" diff --git a/setup.py b/setup.py index ffe45e9..937edee 100755 --- a/setup.py +++ b/setup.py @@ -41,6 +41,9 @@ ], ) ] - extensions = cythonize(extensions) + extensions = cythonize( + extensions, + language_level=3, + ) setup(ext_modules=extensions) diff --git a/src/aperture.c b/src/aperture.c index c11e01c..4d32c5d 100644 --- a/src/aperture.c +++ b/src/aperture.c @@ -789,8 +789,8 @@ int sep_windowed(const sep_image *im, double maskarea, maskweight, maskdxpos, maskdypos; double r, tv, twv, sigtv, totarea, overlap, rpix2, invtwosig2; double wpix; - int64_t i, ix, iy, xmin, xmax, ymin, ymax, sx, sy, pos, size, esize, msize; - int status; + int64_t ix, iy, xmin, xmax, ymin, ymax, sx, sy, pos, size, esize, msize; + int i, status; short errisarray, errisstd; const BYTE *datat, *errort, *maskt; converter convert, econvert, mconvert; diff --git a/src/background.c b/src/background.c index cee2db0..101cce5 100644 --- a/src/background.c +++ b/src/background.c @@ -536,7 +536,7 @@ int filterback(sep_bkg *bkg, int64_t fw, int64_t fh, double fthresh) { /*------ Seek the closest valid mesh */ d2min = BIG; - nmin = 0.0; + nmin = 0; for (j=0,y=0; y-BIG) diff --git a/src/extract.c b/src/extract.c index be12a5c..068c583 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1086,14 +1086,15 @@ PIXTYPE get_mean_thresh(infostruct *info, pliststruct *pixel) { pliststruct *pixt; int pix_accum=0; - PIXTYPE thresh_accum=0; + double thresh_accum=0; + // Threshold must be cast to double to avoid precision loss for (pixt=pixel+info->firstpix; pixt>=pixel; pixt=pixel+PLIST(pixt,nextpix)) { - thresh_accum += PLISTPIX(pixt,thresh); + thresh_accum += (double) PLISTPIX(pixt,thresh); pix_accum++; } - return thresh_accum / pix_accum; + return (PIXTYPE) (thresh_accum / pix_accum); } diff --git a/test.py b/test.py index 7cf6f24..c5b7213 100755 --- a/test.py +++ b/test.py @@ -373,7 +373,7 @@ def test_extract_with_noise_array(): # Threshold must be removed, as it is calculated differently in variable # noise situations - see PR#146 for more details - names_to_remove = ["errx2", "erry2", "errxy", "thresh"] + names_to_remove = ["errx2", "erry2", "errxy"] names_to_keep = [i for i in objects.dtype.names if i not in names_to_remove] objects = objects[names_to_keep] objects2 = objects2[names_to_keep] @@ -384,7 +384,7 @@ def test_extract_with_noise_array(): noise = bkg.globalrms * np.ones_like(data) objects2 = sep.extract(data, 1.5, err=noise, filter_kernel=None) - names_to_remove = ["errx2", "erry2", "errxy", "thresh"] + names_to_remove = ["errx2", "erry2", "errxy"] names_to_keep = [i for i in objects.dtype.names if i not in names_to_remove] objects = objects[names_to_keep] objects2 = objects2[names_to_keep] From 0f7521eb7e4005f89772eae05f4054a28889a110 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:05:48 +0100 Subject: [PATCH 26/47] Many bug fixes, improved tests. --- CHANGES.md | 9 +++++++ docs/changelog.rst | 7 +++++ docs/conf.py | 1 + docs/index.rst | 1 + pyproject.toml | 1 + sep_pjw.pyx | 9 +++++++ src/extract.c | 22 ++++++++-------- test.py | 66 ++++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 docs/changelog.rst diff --git a/CHANGES.md b/CHANGES.md index 02d85ce..398b096 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +v1.3.2.1 (7 February 2023) +======================== + +* Add changelog to documentation. +* Add tests for re-running with seg map. +* Fix array boundary bugs when re-running with seg map. +* Fix bug with precision loss when calculating threshold. +* Improve error handling when object pixels exceed pix stack. + v1.3.2 (5 February 2023) ======================== diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..bdcdf9d --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,7 @@ +.. _changelog: + +Changelog +========= + +.. include:: ../CHANGES.md + :parser: myst_parser.sphinx_ diff --git a/docs/conf.py b/docs/conf.py index 2cf77f4..df60a84 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,6 +49,7 @@ "nbsphinx", "IPython.sphinxext.ipython_console_highlighting", "sphinx_copybutton", + "myst_parser", ] numpydoc_show_class_members = False autosummary_generate = ["reference.rst"] diff --git a/docs/index.rst b/docs/index.rst index c7d75a3..7f8131a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -100,6 +100,7 @@ Usage Guide tutorial filter apertures + changelog .. toctree:: :hidden: diff --git a/pyproject.toml b/pyproject.toml index 8f93fe1..7b37849 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ docs = [ "pandoc>=2.3", "fitsio>=1.2.1", "jupyter", + "myst-parser>2.0.0", ] [project.urls] diff --git a/sep_pjw.pyx b/sep_pjw.pyx index c8ca2b6..4be0c47 100644 --- a/sep_pjw.pyx +++ b/sep_pjw.pyx @@ -723,6 +723,15 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, filter_ids = ids>0 segids = np.ascontiguousarray(ids[filter_ids].astype(dtype=np.int64)) idcounts = np.ascontiguousarray(counts[filter_ids].astype(dtype=np.int64)) + if np.nansum(idcounts)>get_extract_pixstack(): + raise ValueError( + f"The number of object pixels ({np.nansum(idcounts)}) in " + "the segmentation map exceeds the allocated pixel stack " + f"({get_extract_pixstack()}). Use " + "`sep_pjw.set_extract_pixstack()` to increase the size, " + "or check that the correct segmentation map has been " + "supplied." + ) idbuf = segids.view(dtype=np.int64) countbuf = idcounts.view(dtype=np.int64) diff --git a/src/extract.c b/src/extract.c index 068c583..d089574 100644 --- a/src/extract.c +++ b/src/extract.c @@ -23,6 +23,7 @@ /* Note: was scan.c in SExtractor. */ #include +#include #include #include #include @@ -224,8 +225,6 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, h = image->h; numids = (image->numids) ? image->numids : 1; - QCALLOC(cumcounts, int64_t, numids, status); - QCALLOC(idinfo, infostruct, numids, status); prevpix = 0; isvarthresh = 0; @@ -238,15 +237,16 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, mem_pixstack = sep_get_extract_pixstack(); if (image->segmap) { + QCALLOC(cumcounts, int64_t, numids, status); + QCALLOC(idinfo, infostruct, numids, status); totnpix = 0; for (i=0; iidcounts[i]; } - if (sizeof(totnpix)>mem_pixstack) { + if (totnpix>mem_pixstack) { goto exit; } - mem_pixstack = totnpix + 1; } /* seed the random number generator consistently on each call to get @@ -348,10 +348,10 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (image->segmap) { sscan = sbuf.midline; - for (i=0; isegmap) { - if (sscan[xl]>0) { + if ((sscan[xl]>0) && (xl != w) && (yl != h)) { if (xl == 0 || xl == w - 1) curpixinfo.flag |= SEP_OBJ_TRUNC; for (ididx=0; ididxsegids[ididx]==(long)sscan[xl]) { + if (image->segids[ididx]==(int64_t)sscan[xl]) { pixt = pixel + prevpix*plistsize; prevpix = cumcounts[ididx] + idinfo[ididx].pixnb; @@ -761,12 +761,12 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, } if (image->segmap) { arraybuffer_free(&sbuf); + free(idinfo); + free(cumcounts); } freedeblend(&deblendctx); free(pixel); free(info); - free(idinfo); - free(cumcounts); free(store); free(marker); free(dummyscan); @@ -817,7 +817,7 @@ int segsortit(infostruct *info, objliststruct *objlist, obj.lastpix = info->lastpix; obj.flag = info->flag; - obj.thresh = get_mean_thresh(info, objlist->plist); + obj.thresh = plistexist_thresh ? get_mean_thresh(info, objlist->plist): objlist->thresh; analyse(0, objlist, 1, gain); diff --git a/test.py b/test.py index c5b7213..a2fadde 100755 --- a/test.py +++ b/test.py @@ -371,8 +371,6 @@ def test_extract_with_noise_array(): data, 1.5 * bkg.globalrms, err=np.ones_like(data), filter_kernel=None ) - # Threshold must be removed, as it is calculated differently in variable - # noise situations - see PR#146 for more details names_to_remove = ["errx2", "erry2", "errxy"] names_to_keep = [i for i in objects.dtype.names if i not in names_to_remove] objects = objects[names_to_keep] @@ -492,6 +490,70 @@ def test_extract_segmentation_map(): assert objects["npix"][i] == (segmap == i + 1).sum() +@pytest.mark.skipif(NO_FITS, reason="no FITS reader") +def test_extract_seg_map_array(): + + # Get some background-subtracted test data: + data = np.copy(image_data) + bkg = sep.Background(data, bw=64, bh=64, fw=3, fh=3) + bkg.subfrom(data) + + noise = bkg.globalrms * np.ones_like(data) + + for err in [None, noise]: + # err=None + # err=noise + + objects, segmap = sep.extract(data, 1.5, err, segmentation_map=True) + + assert type(segmap) is np.ndarray + assert segmap.shape == data.shape + for i in range(len(objects)): + assert objects["npix"][i] == (segmap == i + 1).sum() + + objects2, segmap2 = sep.extract(data, 1.5, err, segmentation_map=segmap) + + # Test the values for which we expect an exact match + names_exact_match = [ + "thresh", + "npix", + "tnpix", + "xmin", + "xmax", + "ymin", + "ymax", + "cflux", + "flux", + "cpeak", + "peak", + "xcpeak", + "ycpeak", + "xpeak", + "ypeak", + ] + + # The position depends on the object being deblended. As no deblending + # is performed when a segmentation map is supplied, all derived + # parameters may vary slightly. We test those for which we have a + # measurement of the uncertainty + names_close = ["x", "y"] + names_close_var = ["x2", "y2"] + + assert segmap2.shape == data.shape + for o_i, o_ii in zip(objects, objects2): + o_i_exact = o_i[names_exact_match] + o_ii_exact = o_ii[names_exact_match] + assert_equal(o_i_exact, o_ii_exact) + + o_i_close = o_i[names_close] + o_ii_close = o_ii[names_close] + for n, v in zip(names_close, names_close_var): + if o_i["flag"] == 0: + assert_equal(o_i[n], o_ii[n]) + else: + assert_allclose(o_i[n], o_ii[n], atol=np.sqrt(o_i[v])) + + # ----------------------------------------------------------------------------- # aperture tests From 9346f10a5d471f15f4a539049a8412ff669f754d Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 7 Feb 2024 18:28:59 +0100 Subject: [PATCH 27/47] Fix typo in docs and C types. --- pyproject.toml | 2 +- src/extract.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7b37849..6bbf11c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ docs = [ "pandoc>=2.3", "fitsio>=1.2.1", "jupyter", - "myst-parser>2.0.0", + "myst-parser>=2.0.0", ] [project.urls] diff --git a/src/extract.c b/src/extract.c index d089574..fbbdea2 100644 --- a/src/extract.c +++ b/src/extract.c @@ -244,7 +244,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, cumcounts[i] = totnpix; totnpix += image->idcounts[i]; } - if (totnpix>mem_pixstack) { + if ((size_t)totnpix>mem_pixstack) { goto exit; } } @@ -348,7 +348,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (image->segmap) { sscan = sbuf.midline; - for (i=0; i<(size_t)numids; i++) { + for (i=0; i Date: Thu, 8 Feb 2024 15:43:24 +0100 Subject: [PATCH 28/47] Fix headers and includes. --- .clang-format | 108 +++++++++++++ .pre-commit-config.yaml | 19 ++- CHANGES.md | 2 +- ctest/test_image.c | 350 ++++++++++++++++++++++------------------ src/convolve.c | 2 - src/extract.h | 2 + src/lutz.c | 1 - src/overlap.h | 5 +- src/sep.h | 3 + src/sepcore.h | 2 + src/util.c | 1 - 11 files changed, 322 insertions(+), 173 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..790938e --- /dev/null +++ b/.clang-format @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: MIT +# +# Copyright (c) 2023 Intercreate, Inc. +# Author: J.P. Hutchins +# +# Python(black)-inspired .clang-format for C repositories +# +# Includes Zephyr and Arm macro compatibility + +--- +BasedOnStyle: Google + +AlignAfterOpenBracket: BlockIndent +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AttributeMacros: + - __aligned + - __deprecated + - __packed + - __printf_like + - __syscall + - __syscall_always_inline + - __subsystem +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: MultiLine + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false + BeforeLambdaBody: false + BeforeWhile: false +BitFieldColonSpacing: After +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakStringLiterals: true +ColumnLimit: 88 +DerivePointerAlignment: false +ForEachMacros: + - 'FOR_EACH' + - 'FOR_EACH_FIXED_ARG' + - 'FOR_EACH_IDX' + - 'FOR_EACH_IDX_FIXED_ARG' + - 'FOR_EACH_NONEMPTY_TERM' + - 'RB_FOR_EACH' + - 'RB_FOR_EACH_CONTAINER' + - 'SYS_DLIST_FOR_EACH_CONTAINER' + - 'SYS_DLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_DLIST_FOR_EACH_NODE' + - 'SYS_DLIST_FOR_EACH_NODE_SAFE' + - 'SYS_SFLIST_FOR_EACH_CONTAINER' + - 'SYS_SFLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_SFLIST_FOR_EACH_NODE' + - 'SYS_SFLIST_FOR_EACH_NODE_SAFE' + - 'SYS_SLIST_FOR_EACH_CONTAINER' + - 'SYS_SLIST_FOR_EACH_CONTAINER_SAFE' + - 'SYS_SLIST_FOR_EACH_NODE' + - 'SYS_SLIST_FOR_EACH_NODE_SAFE' + - '_WAIT_Q_FOR_EACH' + - 'Z_FOR_EACH' + - 'Z_FOR_EACH_ENGINE' + - 'Z_FOR_EACH_EXEC' + - 'Z_FOR_EACH_FIXED_ARG' + - 'Z_FOR_EACH_FIXED_ARG_EXEC' + - 'Z_FOR_EACH_IDX' + - 'Z_FOR_EACH_IDX_EXEC' + - 'Z_FOR_EACH_IDX_FIXED_ARG' + - 'Z_FOR_EACH_IDX_FIXED_ARG_EXEC' + - 'Z_GENLIST_FOR_EACH_CONTAINER' + - 'Z_GENLIST_FOR_EACH_CONTAINER_SAFE' + - 'Z_GENLIST_FOR_EACH_NODE' + - 'Z_GENLIST_FOR_EACH_NODE_SAFE' + - 'STRUCT_SECTION_FOREACH' + - 'TYPE_SECTION_FOREACH' +IncludeBlocks: Preserve +IfMacros: + - 'CHECKIF' +IndentCaseBlocks: true +IndentCaseLabels: false +IndentWidth: 2 +InsertBraces: true +MaxEmptyLinesToKeep: 2 +PointerAlignment: Middle +SortIncludes: CaseSensitive +SpaceBeforeParens: ControlStatementsExceptControlMacros +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIFY + - Z_STRINGIFY diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 147760e..e4622a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,13 +16,18 @@ repos: rev: 23.12.1 hooks: - id: black -# - repo: https://github.com/numpy/numpydoc -# rev: v1.6.0 -# hooks: -# - id: numpydoc-validation +- repo: https://github.com/numpy/numpydoc + rev: v1.6.0 + hooks: + - id: numpydoc-validation - repo: https://github.com/sphinx-contrib/sphinx-lint rev: v0.9.1 hooks: - - id: sphinx-lint - args: [--enable=all, --disable=default-role, --max-line-length=75] - files: ^docs/ + - id: sphinx-lint + args: [--enable=all, --disable=default-role, --max-line-length=75] + files: ^docs/ +# - repo: https://github.com/pre-commit/mirrors-clang-format +# rev: v17.0.6 +# hooks: +# - id: clang-format +# types_or: [c++, c, cuda] diff --git a/CHANGES.md b/CHANGES.md index 398b096..17aa4c6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,4 @@ -v1.3.2.1 (7 February 2023) +v1.3.3 (7 February 2023) ======================== * Add changelog to documentation. diff --git a/ctest/test_image.c b/ctest/test_image.c index 1245821..729ba55 100644 --- a/ctest/test_image.c +++ b/ctest/test_image.c @@ -1,59 +1,59 @@ +#include +#include #include #include -#include #include -#include #include + #include "sep.h" -uint64_t gettime_ns() -{ +uint64_t gettime_ns() { struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t)tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000ULL; } -double *ones_dbl(int nx, int ny) -{ +double * ones_dbl(int nx, int ny) { int i, npix; double *im, *imt; - im = (double *)malloc((npix = nx*ny)*sizeof(double)); + im = (double *)malloc((npix = nx * ny) * sizeof(double)); imt = im; - for (i=0; i w)? w: xmax; + xmax = (xmax > w) ? w : xmax; ymin = (int)yc - rmax; - ymin = (ymin < 0)? 0: ymin; + ymin = (ymin < 0) ? 0 : ymin; ymax = (int)yc + rmax; - ymax = (ymax > h)? h: ymax; - - for (y=ymin; y h) ? h : ymax; + for (y = ymin; y < ymax; y++) { + for (x = xmin; x < xmax; x++) { + im[x + w * y] += val; + } + } } -void printbox(float *im, int w, int h, int xmin, int xmax, int ymin, int ymax) +void printbox(float * im, int w, int h, int xmin, int xmax, int ymin, int ymax) /* print image values to the screen in a grid Don't make box size too big!! @@ -84,24 +85,22 @@ void printbox(float *im, int w, int h, int xmin, int xmax, int ymin, int ymax) { int i, j; - for (j = ymin; j < ymax && j < h; j++) - { - for (i = xmin; i < xmax && i < w; i++) - printf("%6.2f ", im[w*j+i]); - printf("\n"); + for (j = ymin; j < ymax && j < h; j++) { + for (i = xmin; i < xmax && i < w; i++) { + printf("%6.2f ", im[w * j + i]); } + printf("\n"); + } } - /* an extremely dumb reader for our specific test FITS file! */ -int read_test_image(char *fname, float **data, int *nx, int *ny) -{ - FILE *f; - char buf[80]; /* buffer to hold a line */ +int read_test_image(char * fname, float ** data, int * nx, int * ny) { + FILE * f; + char buf[80]; /* buffer to hold a line */ long pos; size_t nbytes, nel, i, elsize, nread; - char *rawbytes = NULL; - short *rawdata; + char * rawbytes = NULL; + short * rawdata; char tmp; int status = 0; float bscale, bzero; @@ -118,162 +117,191 @@ int read_test_image(char *fname, float **data, int *nx, int *ny) /* read first line and check if it is a FITS file */ nread = fread(buf, 1, 80, f); - if (nread != 80) { status = 1; goto exit; } - if (strncmp(buf, "SIMPLE = T", 30) != 0) - { - printf("not a FITS file"); - status = 1; - goto exit; - } + if (nread != 80) { + status = 1; + goto exit; + } + if (strncmp(buf, "SIMPLE = T", 30) != 0) { + printf("not a FITS file"); + status = 1; + goto exit; + } /* read rows until we get to END keyword */ - while (strncmp(buf, "END", 3) != 0) - { - nread = fread(buf, 1, 80, f); - if (nread != 80) { status = 1; goto exit; } + while (strncmp(buf, "END", 3) != 0) { + nread = fread(buf, 1, 80, f); + if (nread != 80) { + status = 1; + goto exit; } + } /* move to next 2880 byte boundary. */ - pos = ftell(f) % 2880; /* position in "page" */ - if (pos != 0) fseek(f, 2880 - pos, SEEK_CUR); + pos = ftell(f) % 2880; /* position in "page" */ + if (pos != 0) { + fseek(f, 2880 - pos, SEEK_CUR); + } /* read raw data bytes */ nbytes = nel * elsize; rawbytes = (char *)malloc(nbytes); nread = fread(rawbytes, 1, nbytes, f); - if (nread != nbytes) { status = 1; goto exit; } + if (nread != nbytes) { + status = 1; + goto exit; + } /* swap bytes in raw data (FITS is big-endian) */ - for (i=0; iglobalrms, SEP_THRESH_ABS, - 5, conv, 3, 3, SEP_FILTER_CONV, - 32, 1.0, 1, 1.0, &catalog); + status = sep_extract( + &im, + 1.5 * bkg->globalrms, + SEP_THRESH_ABS, + 5, + conv, + 3, + 3, + SEP_FILTER_CONV, + 32, + 1.0, + 1, + 1.0, + &catalog + ); t1 = gettime_ns(); - if (status) goto exit; - print_time("sep_extract()", t1-t0); + if (status) { + goto exit; + } + print_time("sep_extract()", t1 - t0); /* aperture photometry */ - im.noise = &(bkg->globalrms); /* set image noise level */ + im.noise = &(bkg->globalrms); /* set image noise level */ im.ndtype = SEP_TFLOAT; fluxt = flux = (double *)malloc(catalog->nobj * sizeof(double)); fluxerrt = fluxerr = (double *)malloc(catalog->nobj * sizeof(double)); areat = area = (double *)malloc(catalog->nobj * sizeof(double)); flagt = flag = (short *)malloc(catalog->nobj * sizeof(short)); t0 = gettime_ns(); - for (i=0; inobj; i++, fluxt++, fluxerrt++, flagt++, areat++) - sep_sum_circle(&im, - catalog->x[i], catalog->y[i], 5.0, 0, 5, 0, - fluxt, fluxerrt, areat, flagt); + for (i = 0; i < catalog->nobj; i++, fluxt++, fluxerrt++, flagt++, areat++) { + sep_sum_circle( + &im, catalog->x[i], catalog->y[i], 5.0, 0, 5, 0, fluxt, fluxerrt, areat, flagt + ); + } t1 = gettime_ns(); - printf("sep_sum_circle() [r= 5.0] %6.3f us/aperture\n", - (double)(t1 - t0) / 1000. / catalog->nobj); + printf( + "sep_sum_circle() [r= 5.0] %6.3f us/aperture\n", + (double)(t1 - t0) / 1000. / catalog->nobj + ); /* print results */ printf("writing to file: %s\n", fname2); @@ -284,62 +312,64 @@ int main(int argc, char **argv) fprintf(catout, "# 3 Y_IMAGE (0-indexed)\n"); fprintf(catout, "# 4 FLUX\n"); fprintf(catout, "# 5 FLUXERR\n"); - for (i=0; inobj; i++) - { - fprintf(catout, "%3d %#11.7g %#11.7g %#11.7g %#11.7g\n", - i+1, catalog->x[i], catalog->y[i], flux[i], fluxerr[i]); - } + for (i = 0; i < catalog->nobj; i++) { + fprintf( + catout, + "%3d %#11.7g %#11.7g %#11.7g %#11.7g\n", + i + 1, + catalog->x[i], + catalog->y[i], + flux[i], + fluxerr[i] + ); + } fclose(catout); /* clean-up & exit */ - exit: +exit: sep_bkg_free(bkg); free(data); free(flux); free(fluxerr); free(flag); - if (status) - { - printf("FAILED with status: %d\n", status); - char errtext[512]; - sep_get_errdetail(errtext); - puts(errtext); - } - else - { - printf("test_image passed\n"); - } + if (status) { + printf("FAILED with status: %d\n", status); + char errtext[512]; + sep_get_errdetail(errtext); + puts(errtext); + } else { + printf("test_image passed\n"); + } return status; } - - /***************************************************************************/ - /* aperture photometry */ - - /* - int naper, j; - float *xcs, *ycs; - - im = ones(nx, ny); - naper = 1000; - flux = fluxerr = 0.0; - flag = 0; - - float rs[] = {3., 5., 10., 20.}; - for (j=0; j<4; j++) - { - r = rs[j]; - xcs = uniformf(2.*r, nx - 2.*r, naper); - ycs = uniformf(2.*r, ny - 2.*r, naper); - - printf("sep_apercirc() [r=%4.1f] ", r); - t0 = gettime_ns(); - for (i=0; i -#include -#include #include #include #include "sep.h" diff --git a/src/extract.h b/src/extract.h index d7a75dd..5df3268 100644 --- a/src/extract.h +++ b/src/extract.h @@ -20,6 +20,8 @@ * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#include "sepcore.h" + #define UNKNOWN -1 /* flag for LUTZ */ #define CLEAN_ZONE 10.0 /* zone (in sigma) to consider for processing */ #define CLEAN_STACKSIZE 3000 /* replaces prefs.clean_stacksize */ diff --git a/src/lutz.c b/src/lutz.c index 2a11017..3d63b9e 100644 --- a/src/lutz.c +++ b/src/lutz.c @@ -22,7 +22,6 @@ /* Note: was extract.c in SExtractor. */ -#include #include #include #include diff --git a/src/overlap.h b/src/overlap.h index 7fe3f9a..70c17b3 100644 --- a/src/overlap.h +++ b/src/overlap.h @@ -5,6 +5,9 @@ * Original cython version by Thomas Robitaille. Converted to C by Kyle * Barbary. */ +#include +#include "sepcore.h" + #if defined(_MSC_VER) #define INLINE _inline #else @@ -326,7 +329,7 @@ static point circle_segment_single2(double x1, double y1, double x2, double y2) /* Intersection(s) of a segment with the unit circle. Discard any solution not on the segment. */ -intersections circle_segment(double x1, double y1, double x2, double y2) +inline intersections circle_segment(double x1, double y1, double x2, double y2) { intersections inter, inter_new; point pt1, pt2; diff --git a/src/sep.h b/src/sep.h index 1615440..4d2bd56 100644 --- a/src/sep.h +++ b/src/sep.h @@ -20,6 +20,9 @@ * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#include +#include + #ifdef _MSC_VER #define SEP_API __declspec(dllexport) #else diff --git a/src/sepcore.h b/src/sepcore.h index 5159266..3e2782c 100644 --- a/src/sepcore.h +++ b/src/sepcore.h @@ -20,6 +20,8 @@ * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#include + #define RETURN_OK 0 /* must be zero */ #define MEMORY_ALLOC_ERROR 1 #define PIXSTACK_FULL 2 diff --git a/src/util.c b/src/util.c index f387e82..41be2a2 100644 --- a/src/util.c +++ b/src/util.c @@ -15,7 +15,6 @@ * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#include #include #include #include From 6c24809bbe0181c97ef66d8e3f25a888e6ed4643 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:16:46 +0100 Subject: [PATCH 29/47] More formatting. --- .pre-commit-config.yaml | 10 +- src/analyse.c | 397 ++++++------ src/aperture.c | 1271 ++++++++++++++++++------------------ src/background.c | 1357 ++++++++++++++++++++------------------- src/convolve.c | 227 +++---- src/deblend.c | 454 ++++++------- src/extract.c | 1088 ++++++++++++++++--------------- src/extract.h | 251 ++++---- src/lutz.c | 464 +++++++------ src/overlap.h | 665 +++++++++---------- src/sep.h | 424 +++++++----- src/sepcore.h | 153 +++-- src/util.c | 390 +++++------ 13 files changed, 3672 insertions(+), 3479 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e4622a2..7a87cd5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,8 +26,8 @@ repos: - id: sphinx-lint args: [--enable=all, --disable=default-role, --max-line-length=75] files: ^docs/ -# - repo: https://github.com/pre-commit/mirrors-clang-format -# rev: v17.0.6 -# hooks: -# - id: clang-format -# types_or: [c++, c, cuda] +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: v17.0.6 + hooks: + - id: clang-format + types_or: [c++, c, cuda] diff --git a/src/analyse.c b/src/analyse.c index 4b08989..5b5371e 100644 --- a/src/analyse.c +++ b/src/analyse.c @@ -1,33 +1,34 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include #include #include #include #include + +#include "extract.h" #include "sep.h" #include "sepcore.h" -#include "extract.h" /********************************** cleanprep ********************************/ /* @@ -35,120 +36,115 @@ * This used to be in analyse() / examineiso(). */ -int analysemthresh(int objnb, objliststruct *objlist, int minarea, - PIXTYPE thresh) -{ - objstruct *obj = objlist->obj+objnb; - pliststruct *pixel = objlist->plist; - pliststruct *pixt; +int analysemthresh(int objnb, objliststruct * objlist, int minarea, PIXTYPE thresh) { + objstruct * obj = objlist->obj + objnb; + pliststruct * pixel = objlist->plist; + pliststruct * pixt; PIXTYPE tpix; - float *heap,*heapt,*heapj,*heapk, swap; - int j, k, h, status; + float *heap, *heapt, *heapj, *heapk, swap; + int j, k, h, status; status = RETURN_OK; heap = heapt = heapj = heapk = NULL; h = minarea; - if (obj->fdnpix < minarea) - { - obj->mthresh = 0.0; - return status; - } + if (obj->fdnpix < minarea) { + obj->mthresh = 0.0; + return status; + } QCALLOC(heap, float, minarea, status); heapt = heap; /*-- Find the minareath pixel in decreasing intensity for CLEANing */ - for (pixt=pixel+obj->firstpix; pixt>=pixel; pixt=pixel+PLIST(pixt,nextpix)) - { - /* amount pixel is above threshold */ - tpix = PLISTPIX(pixt, cdvalue) - (PLISTEXIST(thresh)? - PLISTPIX(pixt, thresh):thresh); - if (h>0) - *(heapt++) = (float)tpix; - else if (h) - { - if ((float)tpix>*heap) - { - *heap = (float)tpix; - for (j=0; (k=(j+1)<<1)<=minarea; j=k) - { - heapk = heap+k; - heapj = heap+j; - if (k != minarea && *(heapk-1) > *heapk) - { - heapk++; - k++; - } - if (*heapj <= *(--heapk)) - break; - swap = *heapk; - *heapk = *heapj; - *heapj = swap; - } - } + for (pixt = pixel + obj->firstpix; pixt >= pixel; pixt = pixel + PLIST(pixt, nextpix)) + { + /* amount pixel is above threshold */ + tpix = PLISTPIX(pixt, cdvalue) + - (PLISTEXIST(thresh) ? PLISTPIX(pixt, thresh) : thresh); + if (h > 0) { + *(heapt++) = (float)tpix; + } else if (h) { + if ((float)tpix > *heap) { + *heap = (float)tpix; + for (j = 0; (k = (j + 1) << 1) <= minarea; j = k) { + heapk = heap + k; + heapj = heap + j; + if (k != minarea && *(heapk - 1) > *heapk) { + heapk++; + k++; + } + if (*heapj <= *(--heapk)) { + break; + } + swap = *heapk; + *heapk = *heapj; + *heapj = swap; } - else - fqmedian(heap, minarea); - h--; + } + } else { + fqmedian(heap, minarea); } + h--; + } obj->mthresh = *heap; - exit: +exit: free(heap); return status; } /************************* preanalyse **************************************/ -void preanalyse(int no, objliststruct *objlist) -{ - objstruct *obj = &objlist->obj[no]; - pliststruct *pixel = objlist->plist, *pixt; - PIXTYPE peak, cpeak, val, cval; - double rv; - int64_t x, y, xmin,xmax, ymin,ymax, fdnpix; - int64_t xpeak, ypeak, xcpeak, ycpeak; +void preanalyse(int no, objliststruct * objlist) { + objstruct * obj = &objlist->obj[no]; + pliststruct *pixel = objlist->plist, *pixt; + PIXTYPE peak, cpeak, val, cval; + double rv; + int64_t x, y, xmin, xmax, ymin, ymax, fdnpix; + int64_t xpeak, ypeak, xcpeak, ycpeak; /*----- initialize stacks and bounds */ fdnpix = 0; rv = 0.0; peak = cpeak = -BIG; - ymin = xmin = 2*MAXPICSIZE; /* to be really sure!! */ + ymin = xmin = 2 * MAXPICSIZE; /* to be really sure!! */ ymax = xmax = 0; xpeak = ypeak = xcpeak = ycpeak = 0; /* avoid -Wall warnings */ /*----- integrate results */ - for (pixt=pixel+obj->firstpix; pixt>=pixel; pixt=pixel+PLIST(pixt,nextpix)) - { - x = PLIST(pixt, x); - y = PLIST(pixt, y); - val = PLISTPIX(pixt, value); - cval = PLISTPIX(pixt, cdvalue); - if (peak < val) - { - peak = val; - xpeak = x; - ypeak = y; - } - if (cpeak < cval) - { - cpeak = cval; - xcpeak = x; - ycpeak = y; - } - rv += cval; - if (xmin > x) - xmin = x; - if (xmax < x) - xmax = x; - if (ymin > y) - ymin = y; - if (ymax < y) - ymax = y; - fdnpix++; + for (pixt = pixel + obj->firstpix; pixt >= pixel; pixt = pixel + PLIST(pixt, nextpix)) + { + x = PLIST(pixt, x); + y = PLIST(pixt, y); + val = PLISTPIX(pixt, value); + cval = PLISTPIX(pixt, cdvalue); + if (peak < val) { + peak = val; + xpeak = x; + ypeak = y; } + if (cpeak < cval) { + cpeak = cval; + xcpeak = x; + ycpeak = y; + } + rv += cval; + if (xmin > x) { + xmin = x; + } + if (xmax < x) { + xmax = x; + } + if (ymin > y) { + ymin = y; + } + if (ymax < y) { + ymax = y; + } + fdnpix++; + } obj->fdnpix = fdnpix; obj->fdflux = (float)rv; @@ -169,17 +165,13 @@ void preanalyse(int no, objliststruct *objlist) If robust = 1, you must have run previously with robust=0 */ -void analyse(int no, objliststruct *objlist, int robust, double gain) -{ - objstruct *obj = &objlist->obj[no]; - pliststruct *pixel = objlist->plist, *pixt; - PIXTYPE peak, val, cval; - double thresh,thresh2, t1t2,darea, - mx,my, mx2,my2,mxy, rv, rv2, tv, - xm,ym, xm2,ym2,xym, - temp,temp2, theta,pmx2,pmy2, - errx2, erry2, errxy, cvar, cvarsum; - int64_t x, y, xmin, ymin, area2, dnpix; +void analyse(int no, objliststruct * objlist, int robust, double gain) { + objstruct * obj = &objlist->obj[no]; + pliststruct *pixel = objlist->plist, *pixt; + PIXTYPE peak, val, cval; + double thresh, thresh2, t1t2, darea, mx, my, mx2, my2, mxy, rv, rv2, tv, xm, ym, xm2, + ym2, xym, temp, temp2, theta, pmx2, pmy2, errx2, erry2, errxy, cvar, cvarsum; + int64_t x, y, xmin, ymin, area2, dnpix; preanalyse(no, objlist); @@ -191,109 +183,108 @@ void analyse(int no, objliststruct *objlist, int robust, double gain) peak = obj->dpeak; rv = obj->fdflux; rv2 = rv * rv; - thresh2 = (thresh + peak)/2.0; + thresh2 = (thresh + peak) / 2.0; area2 = 0; xmin = obj->xmin; ymin = obj->ymin; - for (pixt=pixel+obj->firstpix; pixt>=pixel; pixt=pixel+PLIST(pixt,nextpix)) - { - x = PLIST(pixt,x)-xmin; /* avoid roundoff errors on big images */ - y = PLIST(pixt,y)-ymin; /* avoid roundoff errors on big images */ - cval = PLISTPIX(pixt, cdvalue); - tv += (val = PLISTPIX(pixt, value)); - if (val>thresh) - dnpix++; - if (val > thresh2) - area2++; - mx += cval * x; - my += cval * y; - mx2 += cval * x*x; - my2 += cval * y*y; - mxy += cval * x*y; - + for (pixt = pixel + obj->firstpix; pixt >= pixel; pixt = pixel + PLIST(pixt, nextpix)) + { + x = PLIST(pixt, x) - xmin; /* avoid roundoff errors on big images */ + y = PLIST(pixt, y) - ymin; /* avoid roundoff errors on big images */ + cval = PLISTPIX(pixt, cdvalue); + tv += (val = PLISTPIX(pixt, value)); + if (val > thresh) { + dnpix++; + } + if (val > thresh2) { + area2++; } + mx += cval * x; + my += cval * y; + mx2 += cval * x * x; + my2 += cval * y * y; + mxy += cval * x * y; + } /* compute object's properties */ - xm = mx / rv; /* mean x */ - ym = my / rv; /* mean y */ + xm = mx / rv; /* mean x */ + ym = my / rv; /* mean y */ /* In case of blending, use previous barycenters */ - if ((robust) && (obj->flag & SEP_OBJ_MERGED)) - { - double xn, yn; - - xn = obj->mx-xmin; - yn = obj->my-ymin; - xm2 = mx2 / rv + xn*xn - 2*xm*xn; - ym2 = my2 / rv + yn*yn - 2*ym*yn; - xym = mxy / rv + xn*yn - xm*yn - xn*ym; - xm = xn; - ym = yn; - } - else - { - xm2 = mx2 / rv - xm * xm; /* variance of x */ - ym2 = my2 / rv - ym * ym; /* variance of y */ - xym = mxy / rv - xm * ym; /* covariance */ - - } + if ((robust) && (obj->flag & SEP_OBJ_MERGED)) { + double xn, yn; + + xn = obj->mx - xmin; + yn = obj->my - ymin; + xm2 = mx2 / rv + xn * xn - 2 * xm * xn; + ym2 = my2 / rv + yn * yn - 2 * ym * yn; + xym = mxy / rv + xn * yn - xm * yn - xn * ym; + xm = xn; + ym = yn; + } else { + xm2 = mx2 / rv - xm * xm; /* variance of x */ + ym2 = my2 / rv - ym * ym; /* variance of y */ + xym = mxy / rv - xm * ym; /* covariance */ + } /* Calculate the errors on the variances */ - for (pixt=pixel+obj->firstpix; pixt>=pixel; pixt=pixel+PLIST(pixt,nextpix)) - { - x = PLIST(pixt,x)-xmin; /* avoid roundoff errors on big images */ - y = PLIST(pixt,y)-ymin; /* avoid roundoff errors on big images */ - - cvar = PLISTEXIST(var)? PLISTPIX(pixt, var): 0.0; - if (gain > 0.0) { /* add poisson noise if given */ - cval = PLISTPIX(pixt, cdvalue); - if (cval > 0.0) cvar += cval / gain; - } + for (pixt = pixel + obj->firstpix; pixt >= pixel; pixt = pixel + PLIST(pixt, nextpix)) + { + x = PLIST(pixt, x) - xmin; /* avoid roundoff errors on big images */ + y = PLIST(pixt, y) - ymin; /* avoid roundoff errors on big images */ - /* Note that this works for both blended and non-blended cases - * because xm is set to xn above for the blended case. */ - cvarsum += cvar; - errx2 += cvar * (x - xm) * (x - xm); - erry2 += cvar * (y - ym) * (y - ym); - errxy += cvar * (x - xm) * (y - ym); + cvar = PLISTEXIST(var) ? PLISTPIX(pixt, var) : 0.0; + if (gain > 0.0) { /* add poisson noise if given */ + cval = PLISTPIX(pixt, cdvalue); + if (cval > 0.0) { + cvar += cval / gain; + } } + + /* Note that this works for both blended and non-blended cases + * because xm is set to xn above for the blended case. */ + cvarsum += cvar; + errx2 += cvar * (x - xm) * (x - xm); + erry2 += cvar * (y - ym) * (y - ym); + errxy += cvar * (x - xm) * (y - ym); + } errx2 /= rv2; erry2 /= rv2; errxy /= rv2; /* Handle fully correlated x/y (which cause a singularity...) */ - if ((temp2=xm2*ym2-xym*xym)<0.00694) - { - xm2 += 0.0833333; - ym2 += 0.0833333; - temp2 = xm2*ym2-xym*xym; - obj->flag |= SEP_OBJ_SINGU; - - /* handle it for the error parameters */ - cvarsum *= 0.08333/rv2; - if (errx2*erry2 - errxy * errxy < cvarsum * cvarsum) { - errx2 += cvarsum; - erry2 += cvarsum; - } + if ((temp2 = xm2 * ym2 - xym * xym) < 0.00694) { + xm2 += 0.0833333; + ym2 += 0.0833333; + temp2 = xm2 * ym2 - xym * xym; + obj->flag |= SEP_OBJ_SINGU; + + /* handle it for the error parameters */ + cvarsum *= 0.08333 / rv2; + if (errx2 * erry2 - errxy * errxy < cvarsum * cvarsum) { + errx2 += cvarsum; + erry2 += cvarsum; } + } - if ((fabs(temp=xm2-ym2)) > 0.0) + if ((fabs(temp = xm2 - ym2)) > 0.0) { theta = atan2(2.0 * xym, temp) / 2.0; - else - theta = PI/4.0; + } else { + theta = PI / 4.0; + } - temp = sqrt(0.25*temp*temp+xym*xym); - pmy2 = pmx2 = 0.5*(xm2+ym2); - pmx2+=temp; - pmy2-=temp; + temp = sqrt(0.25 * temp * temp + xym * xym); + pmy2 = pmx2 = 0.5 * (xm2 + ym2); + pmx2 += temp; + pmy2 -= temp; obj->dnpix = (LONG)dnpix; obj->dflux = tv; - obj->mx = xm+xmin; /* add back xmin */ - obj->my = ym+ymin; /* add back ymin */ + obj->mx = xm + xmin; /* add back xmin */ + obj->my = ym + ymin; /* add back ymin */ obj->mx2 = xm2; obj->errx2 = errx2; obj->my2 = ym2; @@ -304,24 +295,22 @@ void analyse(int no, objliststruct *objlist, int robust, double gain) obj->b = (float)sqrt(pmy2); obj->theta = theta; - obj->cxx = (float)(ym2/temp2); - obj->cyy = (float)(xm2/temp2); - obj->cxy = (float)(-2*xym/temp2); + obj->cxx = (float)(ym2 / temp2); + obj->cyy = (float)(xm2 / temp2); + obj->cxy = (float)(-2 * xym / temp2); darea = (double)area2 - dnpix; - t1t2 = thresh/thresh2; + t1t2 = thresh / thresh2; /* debugging */ - /*if (t1t2>0.0 && !PLISTEXIST(thresh)) */ /* was: prefs.dweight_flag */ - if (t1t2 > 0.0) - { - obj->abcor = (darea<0.0?darea:-1.0)/(2*PI*log(t1t2<1.0?t1t2:0.99) - *obj->a*obj->b); - if (obj->abcor>1.0) - obj->abcor = 1.0; - } - else - { + /*if (t1t2>0.0 && !PLISTEXIST(thresh)) */ /* was: prefs.dweight_flag */ + if (t1t2 > 0.0) { + obj->abcor = (darea < 0.0 ? darea : -1.0) + / (2 * PI * log(t1t2 < 1.0 ? t1t2 : 0.99) * obj->a * obj->b); + if (obj->abcor > 1.0) { obj->abcor = 1.0; } + } else { + obj->abcor = 1.0; + } } diff --git a/src/aperture.c b/src/aperture.c index 4d32c5d..5fb6ad1 100644 --- a/src/aperture.c +++ b/src/aperture.c @@ -1,40 +1,41 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include #include #include #include #include + +#include "overlap.h" #include "sep.h" #include "sepcore.h" -#include "overlap.h" #define FLUX_RADIUS_BUFSIZE 64 -#define WINPOS_NITERMAX 16 /* Maximum number of steps */ -#define WINPOS_NSIG 4 /* Measurement radius */ -#define WINPOS_STEPMIN 0.0001 /* Minimum change in position for continuing */ -#define WINPOS_FAC 2.0 /* Centroid offset factor (2 for a Gaussian) */ +#define WINPOS_NITERMAX 16 /* Maximum number of steps */ +#define WINPOS_NSIG 4 /* Measurement radius */ +#define WINPOS_STEPMIN 0.0001 /* Minimum change in position for continuing */ +#define WINPOS_FAC 2.0 /* Centroid offset factor (2 for a Gaussian) */ /* Adding (void *) pointers is a GNU C extension, not part of standard C. @@ -42,9 +43,9 @@ (void *) to something the size of one byte. */ #if defined(_MSC_VER) - #define MSVC_VOID_CAST (char *) +#define MSVC_VOID_CAST (char *) #else - #define MSVC_VOID_CAST +#define MSVC_VOID_CAST #endif /****************************************************************************/ @@ -58,43 +59,46 @@ cxx*cyy - cxy*cxy/4. > 0. cxx + cyy > 0. */ -int sep_ellipse_axes(double cxx, double cyy, double cxy, - double *a, double *b, double *theta) -{ +int sep_ellipse_axes( + double cxx, double cyy, double cxy, double * a, double * b, double * theta +) { double p, q, t; p = cxx + cyy; q = cxx - cyy; - t = sqrt(q*q + cxy*cxy); + t = sqrt(q * q + cxy * cxy); /* Ensure that parameters actually describe an ellipse. */ - if ((cxx*cyy - cxy*cxy/4. <= 0.) || (p <= 0.)) + if ((cxx * cyy - cxy * cxy / 4. <= 0.) || (p <= 0.)) { return NON_ELLIPSE_PARAMS; + } *a = sqrt(2. / (p - t)); *b = sqrt(2. / (p + t)); /* theta = 0 if cxy == 0, else (1/2) acot(q/cxy) */ - *theta = (cxy == 0.) ? 0. : (q == 0. ? 0. : atan(cxy/q))/2.; - if (cxx>cyy) - *theta += PI/2.; - if (*theta > PI/2.) + *theta = (cxy == 0.) ? 0. : (q == 0. ? 0. : atan(cxy / q)) / 2.; + if (cxx > cyy) { + *theta += PI / 2.; + } + if (*theta > PI / 2.) { *theta -= PI; + } return RETURN_OK; } -void sep_ellipse_coeffs(double a, double b, double theta, - double *cxx, double *cyy, double *cxy) -{ +void sep_ellipse_coeffs( + double a, double b, double theta, double * cxx, double * cyy, double * cxy +) { double costheta, sintheta; costheta = cos(theta); sintheta = sin(theta); - *cxx = costheta*costheta/(a*a) + sintheta*sintheta/(b*b); - *cyy = sintheta*sintheta/(a*a) + costheta*costheta/(b*b); - *cxy = 2.*costheta*sintheta * (1./(a*a) - 1./(b*b)); + *cxx = costheta * costheta / (a * a) + sintheta * sintheta / (b * b); + *cyy = sintheta * sintheta / (a * a) + costheta * costheta / (b * b); + *cxy = 2. * costheta * sintheta * (1. / (a * a) - 1. / (b * b)); } /*****************************************************************************/ @@ -104,69 +108,80 @@ void sep_ellipse_coeffs(double a, double b, double theta, * parameters x, y, r. xmin is inclusive and xmax is exclusive. * Ensures that box is within image bound and sets a flag if it is not. */ -static void boxextent(double x, double y, double rx, double ry, int64_t w, int64_t h, - int64_t *xmin, int64_t *xmax, int64_t *ymin, int64_t *ymax, - short *flag) -{ +static void boxextent( + double x, + double y, + double rx, + double ry, + int64_t w, + int64_t h, + int64_t * xmin, + int64_t * xmax, + int64_t * ymin, + int64_t * ymax, + short * flag +) { *xmin = (int64_t)(x - rx + 0.5); *xmax = (int64_t)(x + rx + 1.4999999); *ymin = (int64_t)(y - ry + 0.5); *ymax = (int64_t)(y + ry + 1.4999999); - if (*xmin < 0) - { - *xmin = 0; - *flag |= SEP_APER_TRUNC; - } - if (*xmax > w) - { - *xmax = w; - *flag |= SEP_APER_TRUNC; - } - if (*ymin < 0) - { - *ymin = 0; - *flag |= SEP_APER_TRUNC; - } - if (*ymax > h) - { - *ymax = h; - *flag |= SEP_APER_TRUNC; - } + if (*xmin < 0) { + *xmin = 0; + *flag |= SEP_APER_TRUNC; + } + if (*xmax > w) { + *xmax = w; + *flag |= SEP_APER_TRUNC; + } + if (*ymin < 0) { + *ymin = 0; + *flag |= SEP_APER_TRUNC; + } + if (*ymax > h) { + *ymax = h; + *flag |= SEP_APER_TRUNC; + } } -static void boxextent_ellipse(double x, double y, - double cxx, double cyy, double cxy, double r, - int64_t w, int64_t h, - int64_t *xmin, int64_t *xmax, int64_t *ymin, int64_t *ymax, - short *flag) -{ +static void boxextent_ellipse( + double x, + double y, + double cxx, + double cyy, + double cxy, + double r, + int64_t w, + int64_t h, + int64_t * xmin, + int64_t * xmax, + int64_t * ymin, + int64_t * ymax, + short * flag +) { double dxlim, dylim; - dxlim = cxx - cxy*cxy/(4.0*cyy); - dxlim = dxlim>0.0 ? r/sqrt(dxlim) : 0.0; - dylim = cyy - cxy*cxy/(4.0*cxx); - dylim = dylim > 0.0 ? r/sqrt(dylim) : 0.0; + dxlim = cxx - cxy * cxy / (4.0 * cyy); + dxlim = dxlim > 0.0 ? r / sqrt(dxlim) : 0.0; + dylim = cyy - cxy * cxy / (4.0 * cxx); + dylim = dylim > 0.0 ? r / sqrt(dylim) : 0.0; boxextent(x, y, dxlim, dylim, w, h, xmin, xmax, ymin, ymax, flag); } /* determine oversampled annulus for a circle */ -static void oversamp_ann_circle(double r, double *r_in2, double *r_out2) -{ - *r_in2 = r - 0.7072; - *r_in2 = (*r_in2 > 0.0) ? (*r_in2)*(*r_in2) : 0.0; - *r_out2 = r + 0.7072; - *r_out2 = (*r_out2) * (*r_out2); +static void oversamp_ann_circle(double r, double * r_in2, double * r_out2) { + *r_in2 = r - 0.7072; + *r_in2 = (*r_in2 > 0.0) ? (*r_in2) * (*r_in2) : 0.0; + *r_out2 = r + 0.7072; + *r_out2 = (*r_out2) * (*r_out2); } /* determine oversampled "annulus" for an ellipse */ -static void oversamp_ann_ellipse(double r, double b, double *r_in2, - double *r_out2) -{ - *r_in2 = r - 0.7072/b; - *r_in2 = (*r_in2 > 0.0) ? (*r_in2)*(*r_in2) : 0.0; - *r_out2 = r + 0.7072/b; - *r_out2 = (*r_out2) * (*r_out2); +static void oversamp_ann_ellipse(double r, double b, double * r_in2, double * r_out2) { + *r_in2 = r - 0.7072 / b; + *r_in2 = (*r_in2 > 0.0) ? (*r_in2) * (*r_in2) : 0.0; + *r_out2 = r + 0.7072 / b; + *r_out2 = (*r_out2) * (*r_out2); } /*****************************************************************************/ @@ -175,17 +190,17 @@ static void oversamp_ann_ellipse(double r, double b, double *r_in2, #define APER_NAME sep_sum_circle #define APER_ARGS double r #define APER_DECL double r2, r_in2, r_out2 -#define APER_CHECKS \ - if (r < 0.0) \ - return ILLEGAL_APER_PARAMS -#define APER_INIT \ - r2 = r*r; \ +#define APER_CHECKS \ + if (r < 0.0) \ + return ILLEGAL_APER_PARAMS +#define APER_INIT \ + r2 = r * r; \ oversamp_ann_circle(r, &r_in2, &r_out2) -#define APER_BOXEXTENT boxextent(x, y, r, r, im->w, im->h, \ - &xmin, &xmax, &ymin, &ymax, flag) -#define APER_EXACT circoverlap(dx-0.5, dy-0.5, dx+0.5, dy+0.5, r) -#define APER_RPIX2 dx*dx + dy*dy -#define APER_RPIX2_SUBPIX dx1*dx1 + dy2 +#define APER_BOXEXTENT \ + boxextent(x, y, r, r, im->w, im->h, &xmin, &xmax, &ymin, &ymax, flag) +#define APER_EXACT circoverlap(dx - 0.5, dy - 0.5, dx + 0.5, dy + 0.5, r) +#define APER_RPIX2 dx * dx + dy * dy +#define APER_RPIX2_SUBPIX dx1 * dx1 + dy2 #define APER_COMPARE1 rpix2 < r_out2 #define APER_COMPARE2 rpix2 > r_in2 #define APER_COMPARE3 rpix2 < r2 @@ -209,21 +224,22 @@ static void oversamp_ann_ellipse(double r, double b, double *r_in2, #define APER_NAME sep_sum_ellipse #define APER_ARGS double a, double b, double theta, double r #define APER_DECL double cxx, cyy, cxy, r2, r_in2, r_out2 -#define APER_CHECKS \ - if (!(r >= 0.0 && b >= 0.0 && a >= b && \ - theta >= -PI/2. && theta <= PI/2.)) \ - return ILLEGAL_APER_PARAMS -#define APER_INIT \ - r2 = r*r; \ - oversamp_ann_ellipse(r, b, &r_in2, &r_out2); \ - sep_ellipse_coeffs(a, b, theta, &cxx, &cyy, &cxy); \ - a *= r; \ +#define APER_CHECKS \ + if (!(r >= 0.0 && b >= 0.0 && a >= b && theta >= -PI / 2. && theta <= PI / 2.)) \ + return ILLEGAL_APER_PARAMS +#define APER_INIT \ + r2 = r * r; \ + oversamp_ann_ellipse(r, b, &r_in2, &r_out2); \ + sep_ellipse_coeffs(a, b, theta, &cxx, &cyy, &cxy); \ + a *= r; \ b *= r -#define APER_BOXEXTENT boxextent_ellipse(x, y, cxx, cyy, cxy, r, im->w, im->h, \ - &xmin, &xmax, &ymin, &ymax, flag) -#define APER_EXACT ellipoverlap(dx-0.5, dy-0.5, dx+0.5, dy+0.5, a, b, theta) -#define APER_RPIX2 cxx*dx*dx + cyy*dy*dy + cxy*dx*dy -#define APER_RPIX2_SUBPIX cxx*dx1*dx1 + cyy*dy2 + cxy*dx1*dy +#define APER_BOXEXTENT \ + boxextent_ellipse( \ + x, y, cxx, cyy, cxy, r, im->w, im->h, &xmin, &xmax, &ymin, &ymax, flag \ + ) +#define APER_EXACT ellipoverlap(dx - 0.5, dy - 0.5, dx + 0.5, dy + 0.5, a, b, theta) +#define APER_RPIX2 cxx * dx * dx + cyy * dy * dy + cxy * dx * dy +#define APER_RPIX2_SUBPIX cxx * dx1 * dx1 + cyy * dy2 + cxy * dx1 * dy #define APER_COMPARE1 rpix2 < r_out2 #define APER_COMPARE2 rpix2 > r_in2 #define APER_COMPARE3 rpix2 < r2 @@ -247,20 +263,21 @@ static void oversamp_ann_ellipse(double r, double b, double *r_in2, #define APER_NAME sep_sum_circann #define APER_ARGS double rin, double rout #define APER_DECL double rin2, rin_in2, rin_out2, rout2, rout_in2, rout_out2 -#define APER_CHECKS \ - if (!(rin >= 0.0 && rout >= rin)) \ - return ILLEGAL_APER_PARAMS -#define APER_INIT \ - rin2 = rin*rin; \ - oversamp_ann_circle(rin, &rin_in2, &rin_out2); \ - rout2 = rout*rout; \ +#define APER_CHECKS \ + if (!(rin >= 0.0 && rout >= rin)) \ + return ILLEGAL_APER_PARAMS +#define APER_INIT \ + rin2 = rin * rin; \ + oversamp_ann_circle(rin, &rin_in2, &rin_out2); \ + rout2 = rout * rout; \ oversamp_ann_circle(rout, &rout_in2, &rout_out2) -#define APER_BOXEXTENT boxextent(x, y, rout, rout, im->w, im->h, \ - &xmin, &xmax, &ymin, &ymax, flag) -#define APER_EXACT (circoverlap(dx-0.5, dy-0.5, dx+0.5, dy+0.5, rout) - \ - circoverlap(dx-0.5, dy-0.5, dx+0.5, dy+0.5, rin)) -#define APER_RPIX2 dx*dx + dy*dy -#define APER_RPIX2_SUBPIX dx1*dx1 + dy2 +#define APER_BOXEXTENT \ + boxextent(x, y, rout, rout, im->w, im->h, &xmin, &xmax, &ymin, &ymax, flag) +#define APER_EXACT \ + (circoverlap(dx - 0.5, dy - 0.5, dx + 0.5, dy + 0.5, rout) \ + - circoverlap(dx - 0.5, dy - 0.5, dx + 0.5, dy + 0.5, rin)) +#define APER_RPIX2 dx * dx + dy * dy +#define APER_RPIX2_SUBPIX dx1 * dx1 + dy2 #define APER_COMPARE1 (rpix2 < rout_out2) && (rpix2 > rin_in2) #define APER_COMPARE2 (rpix2 > rout_in2) || (rpix2 < rin_out2) #define APER_COMPARE3 (rpix2 < rout2) && (rpix2 > rin2) @@ -283,26 +300,28 @@ static void oversamp_ann_ellipse(double r, double b, double *r_in2, #define APER_NAME sep_sum_ellipann #define APER_ARGS double a, double b, double theta, double rin, double rout -#define APER_DECL \ - double cxx, cyy, cxy; \ +#define APER_DECL \ + double cxx, cyy, cxy; \ double rin2, rin_in2, rin_out2, rout2, rout_in2, rout_out2 -#define APER_CHECKS \ - if (!(rin >= 0.0 && rout >= rin && b >= 0.0 && a >= b && \ - theta >= -PI/2. && theta <= PI/2.)) \ - return ILLEGAL_APER_PARAMS -#define APER_INIT \ - rin2 = rin*rin; \ - oversamp_ann_ellipse(rin, b, &rin_in2, &rin_out2); \ - rout2 = rout*rout; \ - oversamp_ann_ellipse(rout, b, &rout_in2, &rout_out2); \ +#define APER_CHECKS \ + if (!(rin >= 0.0 && rout >= rin && b >= 0.0 && a >= b && theta >= -PI / 2. \ + && theta <= PI / 2.)) \ + return ILLEGAL_APER_PARAMS +#define APER_INIT \ + rin2 = rin * rin; \ + oversamp_ann_ellipse(rin, b, &rin_in2, &rin_out2); \ + rout2 = rout * rout; \ + oversamp_ann_ellipse(rout, b, &rout_in2, &rout_out2); \ sep_ellipse_coeffs(a, b, theta, &cxx, &cyy, &cxy) -#define APER_BOXEXTENT boxextent_ellipse(x, y, cxx, cyy, cxy, rout, im->w, im->h, \ - &xmin, &xmax, &ymin, &ymax, flag) -#define APER_EXACT \ - (ellipoverlap(dx-0.5, dy-0.5, dx+0.5, dy+0.5, a*rout, b*rout, theta) - \ - ellipoverlap(dx-0.5, dy-0.5, dx+0.5, dy+0.5, a*rin, b*rin, theta)) -#define APER_RPIX2 cxx*dx*dx + cyy*dy*dy + cxy*dx*dy -#define APER_RPIX2_SUBPIX cxx*dx1*dx1 + cyy*dy2 + cxy*dx1*dy +#define APER_BOXEXTENT \ + boxextent_ellipse( \ + x, y, cxx, cyy, cxy, rout, im->w, im->h, &xmin, &xmax, &ymin, &ymax, flag \ + ) +#define APER_EXACT \ + (ellipoverlap(dx - 0.5, dy - 0.5, dx + 0.5, dy + 0.5, a * rout, b * rout, theta) \ + - ellipoverlap(dx - 0.5, dy - 0.5, dx + 0.5, dy + 0.5, a * rin, b * rin, theta)) +#define APER_RPIX2 cxx * dx * dx + cyy * dy * dy + cxy * dx * dy +#define APER_RPIX2_SUBPIX cxx * dx1 * dx1 + cyy * dy2 + cxy * dx1 * dy #define APER_COMPARE1 (rpix2 < rout_out2) && (rpix2 > rin_in2) #define APER_COMPARE2 (rpix2 > rout_in2) || (rpix2 < rin_out2) #define APER_COMPARE3 (rpix2 < rout2) && (rpix2 > rin2) @@ -326,14 +345,21 @@ static void oversamp_ann_ellipse(double r, double b, double *r_in2, * This is just different enough from the other aperture functions * that it doesn't quite make sense to use aperture.i. */ -int sep_sum_circann_multi(const sep_image *im, - double x, double y, double rmax, int64_t n, - int id, - int subpix, - short inflag, - double *sum, double *sumvar, double *area, - double *maskarea, short *flag) -{ +int sep_sum_circann_multi( + const sep_image * im, + double x, + double y, + double rmax, + int64_t n, + int id, + int subpix, + short inflag, + double * sum, + double * sumvar, + double * area, + double * maskarea, + short * flag +) { PIXTYPE pix, varpix; double dx, dy, dx1, dy2, offset, scale, scale2, tmp, rpix2; int64_t ix, iy, xmin, xmax, ymin, ymax, sx, sy, size, esize, msize, ssize, pos; @@ -345,17 +371,20 @@ int sep_sum_circann_multi(const sep_image *im, int64_t j, ismasked; /* input checks */ - if (rmax < 0.0 || n < 1) + if (rmax < 0.0 || n < 1) { return ILLEGAL_APER_PARAMS; - if (subpix < 1) + } + if (subpix < 1) { return ILLEGAL_SUBPIX; + } /* clear results arrays */ - memset(sum, 0, (size_t)(n*sizeof(double))); - memset(sumvar, 0, (size_t)(n*sizeof(double))); - memset(area, 0, (size_t)(n*sizeof(double))); - if (im->mask) - memset(maskarea, 0, (size_t)(n*sizeof(double))); + memset(sum, 0, (size_t)(n * sizeof(double))); + memset(sumvar, 0, (size_t)(n * sizeof(double))); + memset(area, 0, (size_t)(n * sizeof(double))); + if (im->mask) { + memset(maskarea, 0, (size_t)(n * sizeof(double))); + } /* initializations */ size = esize = msize = ssize = 0; @@ -363,14 +392,14 @@ int sep_sum_circann_multi(const sep_image *im, errort = im->noise; *flag = 0; varpix = 0.0; - scale = 1.0/subpix; - scale2 = scale*scale; - offset = 0.5*(scale-1.0); + scale = 1.0 / subpix; + scale2 = scale * scale; + offset = 0.5 * (scale - 1.0); r_out = rmax + 1.5; /* margin for interpolation */ r_out2 = r_out * r_out; - step = rmax/n; - stepdens = 1.0/step; + step = rmax / n; + stepdens = 1.0 / step; prevbinmargin = 0.7072; nextbinmargin = step - 0.7072; j = 0; @@ -380,216 +409,212 @@ int sep_sum_circann_multi(const sep_image *im, errisstd = 0; /* get data converter(s) for input array(s) */ - if ((status = get_converter(im->dtype, &convert, &size))) + if ((status = get_converter(im->dtype, &convert, &size))) { return status; - if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) + } + if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) { return status; - if (im->segmap && (status = get_converter(im->sdtype, &sconvert, &ssize))) + } + if (im->segmap && (status = get_converter(im->sdtype, &sconvert, &ssize))) { return status; + } /* get image noise */ - if (im->noise_type != SEP_NOISE_NONE) - { - errisstd = (im->noise_type == SEP_NOISE_STDDEV); - if (im->noise) - { - errisarray = 1; - if ((status = get_converter(im->ndtype, &econvert, &esize))) - return status; - } - else - { - varpix = (errisstd)? im->noiseval * im->noiseval: im->noiseval; - } + if (im->noise_type != SEP_NOISE_NONE) { + errisstd = (im->noise_type == SEP_NOISE_STDDEV); + if (im->noise) { + errisarray = 1; + if ((status = get_converter(im->ndtype, &econvert, &esize))) { + return status; + } + } else { + varpix = (errisstd) ? im->noiseval * im->noiseval : im->noiseval; } + } /* get extent of box */ - boxextent(x, y, r_out, r_out, im->w, im->h, &xmin, &xmax, &ymin, &ymax, - flag); + boxextent(x, y, r_out, r_out, im->w, im->h, &xmin, &xmax, &ymin, &ymax, flag); /* loop over rows in the box */ - for (iy=ymin; iyh) * im->w + xmin; - datat = MSVC_VOID_CAST im->data + pos*size; - if (errisarray) - errort = MSVC_VOID_CAST im->noise + pos*esize; - if (im->mask) - maskt = MSVC_VOID_CAST im->mask + pos*msize; - if (im->segmap) - segt = MSVC_VOID_CAST im->segmap + pos*ssize; + for (iy = ymin; iy < ymax; iy++) { + /* set pointers to the start of this row */ + pos = (iy % im->h) * im->w + xmin; + datat = MSVC_VOID_CAST im->data + pos * size; + if (errisarray) { + errort = MSVC_VOID_CAST im->noise + pos * esize; + } + if (im->mask) { + maskt = MSVC_VOID_CAST im->mask + pos * msize; + } + if (im->segmap) { + segt = MSVC_VOID_CAST im->segmap + pos * ssize; + } - /* loop over pixels in this row */ - for (ix=xmin; ixmask) - { - if (mconvert(maskt) > im->maskthresh) - { - *flag |= SEP_APER_HASMASKED; - ismasked = 1; - } - } + ismasked = 0; + if (im->mask) { + if (mconvert(maskt) > im->maskthresh) { + *flag |= SEP_APER_HASMASKED; + ismasked = 1; + } + } - /* Segmentation image: - - If `id` is negative, require segmented pixels within the - aperture. - - If `id` is positive, mask pixels with nonzero segment ids - not equal to `id`. - - */ - if (im->segmap) - { - if (id > 0) - { - if ((sconvert(segt) > 0.) & (sconvert(segt) != id)) - { - *flag |= SEP_APER_HASMASKED; - ismasked = 1; - } - } else { - if (sconvert(segt) != -1*id) - { - *flag |= SEP_APER_HASMASKED; - ismasked = 1; - } - } - } - - /* check if oversampling is needed (close to bin boundary?) */ - rpix = sqrt(rpix2); - d = fmod(rpix, step); - if (d < prevbinmargin || d > nextbinmargin) - { - dx += offset; - dy += offset; - for (sy=subpix; sy--; dy+=scale) - { - dx1 = dx; - dy2 = dy*dy; - for (sx=subpix; sx--; dx1+=scale) - { - j = (int64_t)(sqrt(dx1*dx1+dy2)*stepdens); - if (j < n) - { - if (ismasked) - maskarea[j] += scale2; - else - { - sum[j] += scale2*pix; - sumvar[j] += scale2*varpix; - } - area[j] += scale2; - } - } - } - } - else - /* pixel not close to bin boundary */ - { - j = (int64_t)(rpix*stepdens); - if (j < n) - { - if (ismasked) - maskarea[j] += 1.0; - else - { - sum[j] += pix; - sumvar[j] += varpix; - } - area[j] += 1.0; - } + /* Segmentation image: + + If `id` is negative, require segmented pixels within the + aperture. + + If `id` is positive, mask pixels with nonzero segment ids + not equal to `id`. + + */ + if (im->segmap) { + if (id > 0) { + if ((sconvert(segt) > 0.) & (sconvert(segt) != id)) { + *flag |= SEP_APER_HASMASKED; + ismasked = 1; + } + } else { + if (sconvert(segt) != -1 * id) { + *flag |= SEP_APER_HASMASKED; + ismasked = 1; + } + } + } + + /* check if oversampling is needed (close to bin boundary?) */ + rpix = sqrt(rpix2); + d = fmod(rpix, step); + if (d < prevbinmargin || d > nextbinmargin) { + dx += offset; + dy += offset; + for (sy = subpix; sy--; dy += scale) { + dx1 = dx; + dy2 = dy * dy; + for (sx = subpix; sx--; dx1 += scale) { + j = (int64_t)(sqrt(dx1 * dx1 + dy2) * stepdens); + if (j < n) { + if (ismasked) { + maskarea[j] += scale2; + } else { + sum[j] += scale2 * pix; + sumvar[j] += scale2 * varpix; } - } /* closes "if pixel might be within aperture" */ - - /* increment pointers by one element */ - datat += size; - if (errisarray) - errort += esize; - maskt += msize; - segt += ssize; + area[j] += scale2; + } + } + } + } else + /* pixel not close to bin boundary */ + { + j = (int64_t)(rpix * stepdens); + if (j < n) { + if (ismasked) { + maskarea[j] += 1.0; + } else { + sum[j] += pix; + sumvar[j] += varpix; + } + area[j] += 1.0; + } } + } /* closes "if pixel might be within aperture" */ + + /* increment pointers by one element */ + datat += size; + if (errisarray) { + errort += esize; + } + maskt += msize; + segt += ssize; } + } /* correct for masked values */ - if (im->mask) - { - if (inflag & SEP_MASK_IGNORE) - for (j=n; j--;) - area[j] -= maskarea[j]; - else - { - for (j=n; j--;) - { - tmp = area[j] == maskarea[j]? 0.0: area[j]/(area[j]-maskarea[j]); - sum[j] *= tmp; - sumvar[j] *= tmp; - } - } + if (im->mask) { + if (inflag & SEP_MASK_IGNORE) { + for (j = n; j--;) { + area[j] -= maskarea[j]; + } + } else { + for (j = n; j--;) { + tmp = area[j] == maskarea[j] ? 0.0 : area[j] / (area[j] - maskarea[j]); + sum[j] *= tmp; + sumvar[j] *= tmp; + } } + } /* add poisson noise, only if gain > 0 */ - if (im->gain > 0.0) - for (j=n; j--;) - if (sum[j] > 0.0) + if (im->gain > 0.0) { + for (j = n; j--;) { + if (sum[j] > 0.0) { sumvar[j] += sum[j] / im->gain; + } + } + } return status; } /* for use in flux_radius */ -static double inverse(double xmax, const double *y, int64_t n, double ytarg) -{ +static double inverse(double xmax, const double * y, int64_t n, double ytarg) { double step; int64_t i; - step = xmax/n; + step = xmax / n; i = 0; /* increment i until y[i] is >= to ytarg */ - while (i < n && y[i] < ytarg) + while (i < n && y[i] < ytarg) { i++; + } - if (i == 0) - { - if (ytarg <= 0. || y[0] == 0.) - return 0.; - return step * ytarg/y[0]; + if (i == 0) { + if (ytarg <= 0. || y[0] == 0.) { + return 0.; } - if (i == n) + return step * ytarg / y[0]; + } + if (i == n) { return xmax; + } /* note that y[i-1] corresponds to x=step*i. */ - return step * (i + (ytarg - y[i-1])/(y[i] - y[i-1])); + return step * (i + (ytarg - y[i - 1]) / (y[i] - y[i - 1])); } -int sep_flux_radius(const sep_image *im, - double x, double y, double rmax, int id, - int subpix, short inflag, - const double *fluxtot, const double *fluxfrac, int64_t n, double *r, - short *flag) -{ +int sep_flux_radius( + const sep_image * im, + double x, + double y, + double rmax, + int id, + int subpix, + short inflag, + const double * fluxtot, + const double * fluxfrac, + int64_t n, + double * r, + short * flag +) { int status; int64_t i; double f; @@ -599,31 +624,52 @@ int sep_flux_radius(const sep_image *im, double maskareabuf[FLUX_RADIUS_BUFSIZE]; /* measure FLUX_RADIUS_BUFSIZE annuli out to rmax. */ - status = sep_sum_circann_multi(im, x, y, rmax, - FLUX_RADIUS_BUFSIZE, id, subpix, inflag, - sumbuf, sumvarbuf, areabuf, maskareabuf, - flag); + status = sep_sum_circann_multi( + im, + x, + y, + rmax, + FLUX_RADIUS_BUFSIZE, + id, + subpix, + inflag, + sumbuf, + sumvarbuf, + areabuf, + maskareabuf, + flag + ); /* sum up sumbuf array */ - for (i=1; idtype, &convert, &size))) + if ((status = get_converter(im->dtype, &convert, &size))) { return status; - if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) - return status; - if (im->segmap && (status = get_converter(im->sdtype, &sconvert, &ssize))) - return status; + } + if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) { + return status; + } + if (im->segmap && (status = get_converter(im->sdtype, &sconvert, &ssize))) { + return status; + } /* get extent of ellipse in x and y */ - boxextent_ellipse(x, y, cxx, cyy, cxy, r, im->w, im->h, - &xmin, &xmax, &ymin, &ymax, flag); + boxextent_ellipse( + x, y, cxx, cyy, cxy, r, im->w, im->h, &xmin, &xmax, &ymin, &ymax, flag + ); /* loop over rows in the box */ - for (iy=ymin; iyh) * im->w + xmin; - datat = MSVC_VOID_CAST im->data + pos*size; - if (im->mask) - maskt = MSVC_VOID_CAST im->mask + pos*msize; - if (im->segmap) - segt = MSVC_VOID_CAST im->segmap + pos*ssize; + for (iy = ymin; iy < ymax; iy++) { + /* set pointers to the start of this row */ + pos = (iy % im->h) * im->w + xmin; + datat = MSVC_VOID_CAST im->data + pos * size; + if (im->mask) { + maskt = MSVC_VOID_CAST im->mask + pos * msize; + } + if (im->segmap) { + segt = MSVC_VOID_CAST im->segmap + pos * ssize; + } - /* loop over pixels in this row */ - for (ix=xmin; ixmask && mconvert(maskt) > im->maskthresh)) - ismasked = 1; - - /* Segmentation image: - - If `id` is negative, require segmented pixels within the - aperture. - - If `id` is positive, mask pixels with nonzero segment ids - not equal to `id`. - - */ - if (im->segmap) - { - if (id > 0) - { - if ((sconvert(segt) > 0.) & (sconvert(segt) != id)) - { - ismasked = 1; - } - } else { - if (sconvert(segt) != -1*id) - { - ismasked = 1; - } - } - } - - if (ismasked > 0) - { - *flag |= SEP_APER_HASMASKED; - } - else - { - r1 += sqrt(rpix2)*pix; - v1 += pix; - area++; - } + /* loop over pixels in this row */ + for (ix = xmin; ix < xmax; ix++) { + dx = ix - x; + dy = iy - y; + rpix2 = cxx * dx * dx + cyy * dy * dy + cxy * dx * dy; + if (rpix2 <= r2) { + pix = convert(datat); + ismasked = 0; + if ((pix < -BIG) || (im->mask && mconvert(maskt) > im->maskthresh)) { + ismasked = 1; + } + + /* Segmentation image: + + If `id` is negative, require segmented pixels within the + aperture. + + If `id` is positive, mask pixels with nonzero segment ids + not equal to `id`. + + */ + if (im->segmap) { + if (id > 0) { + if ((sconvert(segt) > 0.) & (sconvert(segt) != id)) { + ismasked = 1; + } + } else { + if (sconvert(segt) != -1 * id) { + ismasked = 1; } + } + } - /* increment pointers by one element */ - datat += size; - maskt += msize; - segt += ssize; + if (ismasked > 0) { + *flag |= SEP_APER_HASMASKED; + } else { + r1 += sqrt(rpix2) * pix; + v1 += pix; + area++; } - } + } - if (area == 0) - { - *flag |= SEP_APER_ALLMASKED; - *kronrad = 0.0; - } - else if (r1 <= 0.0 || v1 <= 0.0) - { - *flag |= SEP_APER_NONPOSITIVE; - *kronrad = 0.0; - } - else - { - *kronrad = r1 / v1; + /* increment pointers by one element */ + datat += size; + maskt += msize; + segt += ssize; } + } + + if (area == 0) { + *flag |= SEP_APER_ALLMASKED; + *kronrad = 0.0; + } else if (r1 <= 0.0 || v1 <= 0.0) { + *flag |= SEP_APER_NONPOSITIVE; + *kronrad = 0.0; + } else { + *kronrad = r1 / v1; + } return RETURN_OK; } /* set array values within an ellipse (uc = unsigned char array) */ -void sep_set_ellipse(unsigned char *arr, int64_t w, int64_t h, - double x, double y, double cxx, double cyy, double cxy, - double r, unsigned char val) -{ - unsigned char *arrt; +void sep_set_ellipse( + unsigned char * arr, + int64_t w, + int64_t h, + double x, + double y, + double cxx, + double cyy, + double cxy, + double r, + unsigned char val +) { + unsigned char * arrt; int64_t xmin, xmax, ymin, ymax, xi, yi; double r2, dx, dy, dy2; short flag; /* not actually used, but input to boxextent */ flag = 0; - r2 = r*r; - - boxextent_ellipse(x, y, cxx, cyy, cxy, r, w, h, - &xmin, &xmax, &ymin, &ymax, &flag); - - for (yi=ymin; yinoise; *flag = 0; varpix = 0.0; - scale = 1.0/subpix; - scale2 = scale*scale; - offset = 0.5*(scale-1.0); - invtwosig2 = 1.0/(2.0*sig*sig); + scale = 1.0 / subpix; + scale2 = scale * scale; + offset = 0.5 * (scale - 1.0); + invtwosig2 = 1.0 / (2.0 * sig * sig); errisarray = 0; errisstd = 0; /* Integration radius */ - r = WINPOS_NSIG*sig; + r = WINPOS_NSIG * sig; /* calculate oversampled annulus */ - r2 = r*r; + r2 = r * r; oversamp_ann_circle(r, &r_in2, &r_out2); /* get data converter(s) for input array(s) */ - if ((status = get_converter(im->dtype, &convert, &size))) + if ((status = get_converter(im->dtype, &convert, &size))) { return status; - if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) + } + if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) { return status; + } /* get image noise */ - if (im->noise_type != SEP_NOISE_NONE) - { - errisstd = (im->noise_type == SEP_NOISE_STDDEV); - if (im->noise) - { - errisarray = 1; - if ((status = get_converter(im->ndtype, &econvert, &esize))) - return status; - } - else - { - varpix = (errisstd)? im->noiseval * im->noiseval: im->noiseval; - } + if (im->noise_type != SEP_NOISE_NONE) { + errisstd = (im->noise_type == SEP_NOISE_STDDEV); + if (im->noise) { + errisarray = 1; + if ((status = get_converter(im->ndtype, &econvert, &esize))) { + return status; + } + } else { + varpix = (errisstd) ? im->noiseval * im->noiseval : im->noiseval; } + } /* iteration loop */ - for (i=0; iw, im->h, - &xmin, &xmax, &ymin, &ymax, flag); - - /* TODO: initialize values */ - //mx2ph - //my2ph - // esum, emxy, emx2, emy2, mx2, my2, mxy - tv = twv = sigtv = 0.0; - overlap = totarea = maskarea = maskweight = 0.0; - dxpos = dypos = 0.0; - maskdxpos = maskdypos = 0.0; - - /* loop over rows in the box */ - for (iy=ymin; iyh) * im->w + xmin; - datat = MSVC_VOID_CAST im->data + pos*size; - if (errisarray) - errort = MSVC_VOID_CAST im->noise + pos*esize; - if (im->mask) - maskt = MSVC_VOID_CAST im->mask + pos*msize; - - /* loop over pixels in this row */ - for (ix=xmin; ix r_in2) /* might be partially in aperture */ - { - if (subpix == 0) - overlap = circoverlap(dx-0.5, dy-0.5, dx+0.5, dy+0.5, - r); - else - { - dx += offset; - dy += offset; - overlap = 0.0; - for (sy=subpix; sy--; dy+=scale) - { - dx1 = dx; - dy2 = dy*dy; - for (sx=subpix; sx--; dx1+=scale) - if (dx1*dx1 + dy2 < r2) - overlap += scale2; - } - } - } - else - /* definitely fully in aperture */ - overlap = 1.0; - - /* get pixel value and variance value */ - pix = convert(datat); - if (errisarray) - { - varpix = econvert(errort); - if (errisstd) - varpix *= varpix; - } - - /* offset of this pixel from center */ - dx = ix - x; - dy = iy - y; - - /* weight by gaussian */ - weight = exp(-rpix2*invtwosig2); - - if (im->mask && (mconvert(maskt) > im->maskthresh)) - { - *flag |= SEP_APER_HASMASKED; - maskarea += overlap; - maskweight += overlap * weight; - maskdxpos += overlap * weight * dx; - maskdypos += overlap * weight * dy; - } - else - { - tv += pix * overlap; - wpix = pix * overlap * weight; - twv += wpix; - dxpos += wpix * dx; - dypos += wpix * dy; - } - - totarea += overlap; - - } /* closes "if pixel might be within aperture" */ - - /* increment pointers by one element */ - datat += size; - if (errisarray) - errort += esize; - maskt += msize; - } /* closes loop over x */ - } /* closes loop over y */ - - /* we're done looping over pixels for this iteration. - * Our summary statistics are: - * - * tv : total value - * twv : total weighted value - * dxpos : weighted dx - * dypos : weighted dy - */ - - /* Correct for masked values: This effectively makes it as if - * the masked pixels had the value of the average unmasked value - * in the aperture. - */ - if (im->mask) - { - /* this option will probably not yield accurate values */ - if (inflag & SEP_MASK_IGNORE) - totarea -= maskarea; - else - { - tmp = tv/(totarea-maskarea); /* avg unmasked pixel value */ - twv += tmp * maskweight; - dxpos += tmp * maskdxpos; - dypos += tmp * maskdypos; + for (i = 0; i < WINPOS_NITERMAX; i++) { + /* get extent of box */ + boxextent(x, y, r, r, im->w, im->h, &xmin, &xmax, &ymin, &ymax, flag); + + /* TODO: initialize values */ + // mx2ph + // my2ph + // esum, emxy, emx2, emy2, mx2, my2, mxy + tv = twv = sigtv = 0.0; + overlap = totarea = maskarea = maskweight = 0.0; + dxpos = dypos = 0.0; + maskdxpos = maskdypos = 0.0; + + /* loop over rows in the box */ + for (iy = ymin; iy < ymax; iy++) { + /* set pointers to the start of this row */ + pos = (iy % im->h) * im->w + xmin; + datat = MSVC_VOID_CAST im->data + pos * size; + if (errisarray) { + errort = MSVC_VOID_CAST im->noise + pos * esize; + } + if (im->mask) { + maskt = MSVC_VOID_CAST im->mask + pos * msize; + } + + /* loop over pixels in this row */ + for (ix = xmin; ix < xmax; ix++) { + dx = ix - x; + dy = iy - y; + rpix2 = dx * dx + dy * dy; + if (rpix2 < r_out2) { + if (rpix2 > r_in2) /* might be partially in aperture */ { + if (subpix == 0) { + overlap = circoverlap(dx - 0.5, dy - 0.5, dx + 0.5, dy + 0.5, r); + } else { + dx += offset; + dy += offset; + overlap = 0.0; + for (sy = subpix; sy--; dy += scale) { + dx1 = dx; + dy2 = dy * dy; + for (sx = subpix; sx--; dx1 += scale) { + if (dx1 * dx1 + dy2 < r2) { + overlap += scale2; + } + } + } } - } + } else { + /* definitely fully in aperture */ + overlap = 1.0; + } + + /* get pixel value and variance value */ + pix = convert(datat); + if (errisarray) { + varpix = econvert(errort); + if (errisstd) { + varpix *= varpix; + } + } - /* update center */ - if (twv>0.0) - { - x += (dxpos /= twv) * WINPOS_FAC; - y += (dypos /= twv) * WINPOS_FAC; + /* offset of this pixel from center */ + dx = ix - x; + dy = iy - y; + + /* weight by gaussian */ + weight = exp(-rpix2 * invtwosig2); + + if (im->mask && (mconvert(maskt) > im->maskthresh)) { + *flag |= SEP_APER_HASMASKED; + maskarea += overlap; + maskweight += overlap * weight; + maskdxpos += overlap * weight * dx; + maskdypos += overlap * weight * dy; + } else { + tv += pix * overlap; + wpix = pix * overlap * weight; + twv += wpix; + dxpos += wpix * dx; + dypos += wpix * dy; + } + + totarea += overlap; + + } /* closes "if pixel might be within aperture" */ + + /* increment pointers by one element */ + datat += size; + if (errisarray) { + errort += esize; } - else - break; + maskt += msize; + } /* closes loop over x */ + } /* closes loop over y */ + + /* we're done looping over pixels for this iteration. + * Our summary statistics are: + * + * tv : total value + * twv : total weighted value + * dxpos : weighted dx + * dypos : weighted dy + */ + + /* Correct for masked values: This effectively makes it as if + * the masked pixels had the value of the average unmasked value + * in the aperture. + */ + if (im->mask) { + /* this option will probably not yield accurate values */ + if (inflag & SEP_MASK_IGNORE) { + totarea -= maskarea; + } else { + tmp = tv / (totarea - maskarea); /* avg unmasked pixel value */ + twv += tmp * maskweight; + dxpos += tmp * maskdxpos; + dypos += tmp * maskdypos; + } + } - /* Stop here if position does not change */ - if (dxpos*dxpos+dypos*dypos < WINPOS_STEPMIN*WINPOS_STEPMIN) - break; + /* update center */ + if (twv > 0.0) { + x += (dxpos /= twv) * WINPOS_FAC; + y += (dypos /= twv) * WINPOS_FAC; + } else { + break; + } + + /* Stop here if position does not change */ + if (dxpos * dxpos + dypos * dypos < WINPOS_STEPMIN * WINPOS_STEPMIN) { + break; + } - } /* closes loop over interations */ + } /* closes loop over interations */ /* assign output results */ *xout = x; *yout = y; - *niter = i+1; + *niter = i + 1; return status; } diff --git a/src/background.c b/src/background.c index 101cce5..e726402 100644 --- a/src/background.c +++ b/src/background.c @@ -1,83 +1,106 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include #include #include #include #include + #include "sep.h" #include "sepcore.h" -#define BACK_MINGOODFRAC 0.5 /* min frac with good weights*/ -#define QUANTIF_NSIGMA 5 /* histogram limits */ -#define QUANTIF_NMAXLEVELS 4096 /* max nb of quantif. levels */ -#define QUANTIF_AMIN 4 /* min nb of "mode pixels" */ +#define BACK_MINGOODFRAC 0.5 /* min frac with good weights*/ +#define QUANTIF_NSIGMA 5 /* histogram limits */ +#define QUANTIF_NMAXLEVELS 4096 /* max nb of quantif. levels */ +#define QUANTIF_AMIN 4 /* min nb of "mode pixels" */ /* Background info in a single mesh*/ typedef struct { - float mode, mean, sigma; /* Background mode, mean and sigma */ - int64_t *histo; /* Pointer to a histogram */ - int nlevels; /* Nb of histogram bins */ - float qzero, qscale; /* Position of histogram */ - float lcut, hcut; /* Histogram cuts */ - int64_t npix; /* Number of pixels involved */ + float mode, mean, sigma; /* Background mode, mean and sigma */ + int64_t * histo; /* Pointer to a histogram */ + int nlevels; /* Nb of histogram bins */ + float qzero, qscale; /* Position of histogram */ + float lcut, hcut; /* Histogram cuts */ + int64_t npix; /* Number of pixels involved */ } backstruct; /* internal helper functions */ -void backhisto(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, - int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh); -void backstat(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, - int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh); -int filterback(sep_bkg *bkg, int64_t fw, int64_t fh, double fthresh); -float backguess(backstruct *bkg, float *mean, float *sigma); -int makebackspline(const sep_bkg *bkg, float *map, float *dmap); - - -int sep_background(const sep_image* image, int64_t bw, int64_t bh, int64_t fw, int64_t fh, - double fthresh, sep_bkg **bkg) -{ +void backhisto( + backstruct * backmesh, + const PIXTYPE * buf, + const PIXTYPE * wbuf, + int64_t bufsize, + int64_t n, + int64_t w, + int64_t bw, + PIXTYPE maskthresh +); +void backstat( + backstruct * backmesh, + const PIXTYPE * buf, + const PIXTYPE * wbuf, + int64_t bufsize, + int64_t n, + int64_t w, + int64_t bw, + PIXTYPE maskthresh +); +int filterback(sep_bkg * bkg, int64_t fw, int64_t fh, double fthresh); +float backguess(backstruct * bkg, float * mean, float * sigma); +int makebackspline(const sep_bkg * bkg, float * map, float * dmap); + + +int sep_background( + const sep_image * image, + int64_t bw, + int64_t bh, + int64_t fw, + int64_t fh, + double fthresh, + sep_bkg ** bkg +) { const BYTE *imt, *maskt; - int64_t npix; /* size of image */ - int64_t nx, ny, nb; /* number of background boxes in x, y, total */ - int64_t bufsize; /* size of a "row" of boxes in pixels (w*bh) */ - int64_t elsize; /* size (in bytes) of an image array element */ - int64_t melsize; /* size (in bytes) of a mask array element */ + int64_t npix; /* size of image */ + int64_t nx, ny, nb; /* number of background boxes in x, y, total */ + int64_t bufsize; /* size of a "row" of boxes in pixels (w*bh) */ + int64_t elsize; /* size (in bytes) of an image array element */ + int64_t melsize; /* size (in bytes) of a mask array element */ PIXTYPE *buf, *mbuf; const PIXTYPE *buft, *mbuft; PIXTYPE maskthresh; array_converter convert, mconvert; - backstruct *backmesh, *bm; /* info about each background "box" */ - sep_bkg *bkgout; /* output */ - int64_t j,k,m; + backstruct *backmesh, *bm; /* info about each background "box" */ + sep_bkg * bkgout; /* output */ + int64_t j, k, m; int status; status = RETURN_OK; npix = image->w * image->h; bufsize = image->w * bh; maskthresh = image->maskthresh; - if (image->mask == NULL) maskthresh = 0.0; + if (image->mask == NULL) { + maskthresh = 0.0; + } backmesh = bm = NULL; bkgout = NULL; @@ -86,11 +109,13 @@ int sep_background(const sep_image* image, int64_t bw, int64_t bh, int64_t fw, i convert = mconvert = NULL; /* determine number of background boxes */ - if ((nx = (image->w - 1) / bw + 1) < 1) + if ((nx = (image->w - 1) / bw + 1) < 1) { nx = 1; - if ((ny = (image->h - 1) / bh + 1) < 1) + } + if ((ny = (image->h - 1) / bh + 1) < 1) { ny = 1; - nb = nx*ny; + } + nb = nx * ny; /* Allocate temp memory & initialize */ QCALLOC(backmesh, backstruct, nx, status); @@ -120,31 +145,32 @@ int sep_background(const sep_image* image, int64_t bw, int64_t bh, int64_t fw, i /* get the correct array converter and element size, based on dtype code */ status = get_array_converter(image->dtype, &convert, &elsize); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; - if (image->mask) - { - status = get_array_converter(image->mdtype, &mconvert, &melsize); - if (status != RETURN_OK) - goto exit; + } + if (image->mask) { + status = get_array_converter(image->mdtype, &mconvert, &melsize); + if (status != RETURN_OK) { + goto exit; } + } /* If the input array type is not PIXTYPE, allocate a buffer to hold converted values */ - if (image->dtype != PIXDTYPE) - { - QMALLOC(buf, PIXTYPE, bufsize, status); - buft = buf; - if (status != RETURN_OK) - goto exit; + if (image->dtype != PIXDTYPE) { + QMALLOC(buf, PIXTYPE, bufsize, status); + buft = buf; + if (status != RETURN_OK) { + goto exit; } - if (image->mask && (image->mdtype != PIXDTYPE)) - { - QMALLOC(mbuf, PIXTYPE, bufsize, status); - mbuft = mbuf; - if (status != RETURN_OK) - goto exit; + } + if (image->mask && (image->mdtype != PIXDTYPE)) { + QMALLOC(mbuf, PIXTYPE, bufsize, status); + mbuft = mbuf; + if (status != RETURN_OK) { + goto exit; } + } /* loop over rows of background boxes. * (here, we could loop over individual boxes rather than entire @@ -153,54 +179,57 @@ int sep_background(const sep_image* image, int64_t bw, int64_t bh, int64_t fw, i * because the pixel buffers are only read in from disk in * increments of a row of background boxes at a time.) */ - for (j=0; jdtype != PIXDTYPE) - convert(imt, bufsize, buf); - else - buft = (const PIXTYPE *)imt; - - if (image->mask) - { - if (image->mdtype != PIXDTYPE) - mconvert(maskt, bufsize, mbuf); - else - mbuft = (const PIXTYPE *)maskt; - } - - /* Get clipped mean, sigma for all boxes in the row */ - backstat(backmesh, buft, mbuft, bufsize, nx, image->w, bw, maskthresh); - - /* Allocate histograms in each box in this row. */ - bm = backmesh; - for (m=nx; m--; bm++) - if (bm->mean <= -BIG) - bm->histo=NULL; - else - QCALLOC(bm->histo, int64_t, bm->nlevels, status); - backhisto(backmesh, buft, mbuft, bufsize, nx, image->w, bw, maskthresh); - - /* Compute background statistics from the histograms */ - bm = backmesh; - for (m=0; mback+k, bkgout->sigma+k); - free(bm->histo); - bm->histo = NULL; - } - - /* increment array pointers to next row of background boxes */ - imt += elsize * bufsize; - if (image->mask) - maskt += melsize * bufsize; + for (j = 0; j < ny; j++) { + /* if the last row, modify the width appropriately*/ + if (j == ny - 1 && npix % bufsize) { + bufsize = npix % bufsize; + } + + /* convert this row to PIXTYPE and store in buffer(s)*/ + if (image->dtype != PIXDTYPE) { + convert(imt, bufsize, buf); + } else { + buft = (const PIXTYPE *)imt; + } + + if (image->mask) { + if (image->mdtype != PIXDTYPE) { + mconvert(maskt, bufsize, mbuf); + } else { + mbuft = (const PIXTYPE *)maskt; + } + } + + /* Get clipped mean, sigma for all boxes in the row */ + backstat(backmesh, buft, mbuft, bufsize, nx, image->w, bw, maskthresh); + + /* Allocate histograms in each box in this row. */ + bm = backmesh; + for (m = nx; m--; bm++) { + if (bm->mean <= -BIG) { + bm->histo = NULL; + } else { + QCALLOC(bm->histo, int64_t, bm->nlevels, status); + } + } + backhisto(backmesh, buft, mbuft, bufsize, nx, image->w, bw, maskthresh); + + /* Compute background statistics from the histograms */ + bm = backmesh; + for (m = 0; m < nx; m++, bm++) { + k = m + nx * j; + backguess(bm, bkgout->back + k, bkgout->sigma + k); + free(bm->histo); + bm->histo = NULL; } + /* increment array pointers to next row of background boxes */ + imt += elsize * bufsize; + if (image->mask) { + maskt += melsize * bufsize; + } + } + /* free memory */ free(buf); buf = NULL; @@ -210,30 +239,31 @@ int sep_background(const sep_image* image, int64_t bw, int64_t bh, int64_t fw, i backmesh = NULL; /* Median-filter and check suitability of the background map */ - if ((status = filterback(bkgout, fw, fh, fthresh)) != RETURN_OK) + if ((status = filterback(bkgout, fw, fh, fthresh)) != RETURN_OK) { goto exit; + } /* Compute 2nd derivatives along the y-direction */ - if ((status = makebackspline(bkgout, bkgout->back, bkgout->dback)) != - RETURN_OK) + if ((status = makebackspline(bkgout, bkgout->back, bkgout->dback)) != RETURN_OK) { goto exit; - if ((status = makebackspline(bkgout, bkgout->sigma, bkgout->dsigma)) != - RETURN_OK) + } + if ((status = makebackspline(bkgout, bkgout->sigma, bkgout->dsigma)) != RETURN_OK) { goto exit; + } *bkg = bkgout; return status; /* If we encountered a problem, clean up any allocated memory */ - exit: +exit: free(buf); free(mbuf); - if (backmesh) - { - bm = backmesh; - for (m=0; mhisto); + if (backmesh) { + bm = backmesh; + for (m = 0; m < nx; m++, bm++) { + free(bm->histo); } + } free(backmesh); sep_bkg_free(bkgout); *bkg = NULL; @@ -244,268 +274,275 @@ int sep_background(const sep_image* image, int64_t bw, int64_t bh, int64_t fw, i /* Compute robust statistical estimators in a row of meshes. */ -void backstat(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, - int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh) -{ - backstruct *bm; - double pix, wpix, sig, mean, sigma, step; - const PIXTYPE *buft,*wbuft; - PIXTYPE lcut,hcut; - int64_t m,h,x,y, npix,wnpix, offset, lastbite; - - h = bufsize/w; /* height of background boxes in this row */ +void backstat( + backstruct * backmesh, + const PIXTYPE * buf, + const PIXTYPE * wbuf, + int64_t bufsize, + int64_t n, + int64_t w, + int64_t bw, + PIXTYPE maskthresh +) { + backstruct * bm; + double pix, wpix, sig, mean, sigma, step; + const PIXTYPE *buft, *wbuft; + PIXTYPE lcut, hcut; + int64_t m, h, x, y, npix, wnpix, offset, lastbite; + + h = bufsize / w; /* height of background boxes in this row */ bm = backmesh; offset = w - bw; - step = sqrt(2/PI)*QUANTIF_NSIGMA/QUANTIF_AMIN; - - for (m = n; m--; bm++,buf+=bw) - { - if (!m && (lastbite=w%bw)) - { - bw = lastbite; - offset = w-bw; - } - - mean = sigma = 0.0; - buft=buf; - npix = 0; - - /* We separate the weighted case at this level to avoid penalty in CPU */ - if (wbuf) - { - wbuft = wbuf; - for (y=h; y--; buft+=offset,wbuft+=offset) - for (x=bw; x--;) - { - pix = *(buft++); - if ((wpix = *(wbuft++)) <= maskthresh && pix > -BIG) - { - mean += pix; - sigma += pix*pix; - npix++; - } - } - } - else - for (y=h; y--; buft+=offset) - for (x=bw; x--;) - if ((pix = *(buft++)) > -BIG) - { - mean += pix; - sigma += pix*pix; - npix++; - } - - /*-- If not enough valid pixels, discard this mesh */ - if ((float)npix < (float)(bw*h*BACK_MINGOODFRAC)) - { - bm->mean = bm->sigma = -BIG; - if (wbuf) - wbuf += bw; - continue; - } - - mean /= (double)npix; - sigma = (sig = sigma/npix - mean*mean)>0.0? sqrt(sig):0.0; - lcut = bm->lcut = (PIXTYPE)(mean - 2.0*sigma); - hcut = bm->hcut = (PIXTYPE)(mean + 2.0*sigma); - mean = sigma = 0.0; - npix = wnpix = 0; - buft = buf; - - /* do statistics for this mesh again, with cuts */ - if (wbuf) - { - wbuft=wbuf; - for (y=h; y--; buft+=offset, wbuft+=offset) - for (x=bw; x--;) - { - pix = *(buft++); - if ((wpix = *(wbuft++))<=maskthresh && pix<=hcut && pix>=lcut) - { - mean += pix; - sigma += pix*pix; - npix++; - } - } - } - else - for (y=h; y--; buft+=offset) - for (x=bw; x--;) - { - pix = *(buft++); - if (pix<=hcut && pix>=lcut) - { - mean += pix; - sigma += pix*pix; - npix++; - } - } - - bm->npix = npix; - mean /= (double)npix; - sig = sigma/npix - mean*mean; - sigma = sig>0.0 ? sqrt(sig):0.0; - bm->mean = mean; - bm->sigma = sigma; - if ((bm->nlevels = (int)(step*npix+1)) > QUANTIF_NMAXLEVELS) - bm->nlevels = QUANTIF_NMAXLEVELS; - bm->qscale = sigma>0.0? 2*QUANTIF_NSIGMA*sigma/bm->nlevels : 1.0; - bm->qzero = mean - QUANTIF_NSIGMA*sigma; - - if (wbuf) - wbuf += bw; + step = sqrt(2 / PI) * QUANTIF_NSIGMA / QUANTIF_AMIN; + + for (m = n; m--; bm++, buf += bw) { + if (!m && (lastbite = w % bw)) { + bw = lastbite; + offset = w - bw; + } + + mean = sigma = 0.0; + buft = buf; + npix = 0; + + /* We separate the weighted case at this level to avoid penalty in CPU */ + if (wbuf) { + wbuft = wbuf; + for (y = h; y--; buft += offset, wbuft += offset) { + for (x = bw; x--;) { + pix = *(buft++); + if ((wpix = *(wbuft++)) <= maskthresh && pix > -BIG) { + mean += pix; + sigma += pix * pix; + npix++; + } + } + } + } else { + for (y = h; y--; buft += offset) { + for (x = bw; x--;) { + if ((pix = *(buft++)) > -BIG) { + mean += pix; + sigma += pix * pix; + npix++; + } + } + } } + + /*-- If not enough valid pixels, discard this mesh */ + if ((float)npix < (float)(bw * h * BACK_MINGOODFRAC)) { + bm->mean = bm->sigma = -BIG; + if (wbuf) { + wbuf += bw; + } + continue; + } + + mean /= (double)npix; + sigma = (sig = sigma / npix - mean * mean) > 0.0 ? sqrt(sig) : 0.0; + lcut = bm->lcut = (PIXTYPE)(mean - 2.0 * sigma); + hcut = bm->hcut = (PIXTYPE)(mean + 2.0 * sigma); + mean = sigma = 0.0; + npix = wnpix = 0; + buft = buf; + + /* do statistics for this mesh again, with cuts */ + if (wbuf) { + wbuft = wbuf; + for (y = h; y--; buft += offset, wbuft += offset) { + for (x = bw; x--;) { + pix = *(buft++); + if ((wpix = *(wbuft++)) <= maskthresh && pix <= hcut && pix >= lcut) { + mean += pix; + sigma += pix * pix; + npix++; + } + } + } + } else { + for (y = h; y--; buft += offset) { + for (x = bw; x--;) { + pix = *(buft++); + if (pix <= hcut && pix >= lcut) { + mean += pix; + sigma += pix * pix; + npix++; + } + } + } + } + + bm->npix = npix; + mean /= (double)npix; + sig = sigma / npix - mean * mean; + sigma = sig > 0.0 ? sqrt(sig) : 0.0; + bm->mean = mean; + bm->sigma = sigma; + if ((bm->nlevels = (int)(step * npix + 1)) > QUANTIF_NMAXLEVELS) { + bm->nlevels = QUANTIF_NMAXLEVELS; + } + bm->qscale = sigma > 0.0 ? 2 * QUANTIF_NSIGMA * sigma / bm->nlevels : 1.0; + bm->qzero = mean - QUANTIF_NSIGMA * sigma; + + if (wbuf) { + wbuf += bw; + } + } } /******************************** backhisto *********************************/ /* Fill histograms in a row of meshes. */ -void backhisto(backstruct *backmesh, - const PIXTYPE *buf, const PIXTYPE *wbuf, int64_t bufsize, - int64_t n, int64_t w, int64_t bw, PIXTYPE maskthresh) -{ - backstruct *bm; - const PIXTYPE *buft,*wbuft; - float qscale, cste, wpix; - int64_t *histo; - int64_t h,m,x,y, nlevels, lastbite, offset, bin; - - h = bufsize/w; +void backhisto( + backstruct * backmesh, + const PIXTYPE * buf, + const PIXTYPE * wbuf, + int64_t bufsize, + int64_t n, + int64_t w, + int64_t bw, + PIXTYPE maskthresh +) { + backstruct * bm; + const PIXTYPE *buft, *wbuft; + float qscale, cste, wpix; + int64_t * histo; + int64_t h, m, x, y, nlevels, lastbite, offset, bin; + + h = bufsize / w; bm = backmesh; offset = w - bw; - for (m=0; m++mean <= -BIG) - { - if (wbuf) - wbuf += bw; - continue; - } - - nlevels = bm->nlevels; - histo = bm->histo; - qscale = bm->qscale; - cste = 0.499999 - bm->qzero/qscale; - buft = buf; - - if (wbuf) - { - wbuft = wbuf; - for (y=h; y--; buft+=offset, wbuft+=offset) - for (x=bw; x--;) - { - bin = (int64_t)(*(buft++)/qscale + cste); - if ((wpix = *(wbuft++))<=maskthresh && bin=0) - (*(histo+bin))++; - } - wbuf += bw; - } - else - for (y=h; y--; buft += offset) - for (x=bw; x--;) - { - bin = (int64_t)(*(buft++)/qscale + cste); - - if (bin>=0 && binmean <= -BIG) { + if (wbuf) { + wbuf += bw; + } + continue; + } + + nlevels = bm->nlevels; + histo = bm->histo; + qscale = bm->qscale; + cste = 0.499999 - bm->qzero / qscale; + buft = buf; + + if (wbuf) { + wbuft = wbuf; + for (y = h; y--; buft += offset, wbuft += offset) { + for (x = bw; x--;) { + bin = (int64_t)(*(buft++) / qscale + cste); + if ((wpix = *(wbuft++)) <= maskthresh && bin < nlevels && bin >= 0) { + (*(histo + bin))++; + } + } + } + wbuf += bw; + } else { + for (y = h; y--; buft += offset) { + for (x = bw; x--;) { + bin = (int64_t)(*(buft++) / qscale + cste); + + if (bin >= 0 && bin < nlevels) { + (*(histo + bin))++; + } + } + } } } +} /******************************* backguess **********************************/ /* Estimate the background from a histogram; */ -float backguess(backstruct *bkg, float *mean, float *sigma) - -#define EPS (1e-4) /* a small number */ +float backguess(backstruct * bkg, float * mean, float * sigma) +#define EPS (1e-4) /* a small number */ { - int64_t *histo, *hilow, *hihigh, *histot; + int64_t *histo, *hilow, *hihigh, *histot; unsigned long lowsum, highsum, sum; - double ftemp, mea, sig, sig1, med, dpix; - int64_t i, n, lcut,hcut, nlevelsm1, pix; + double ftemp, mea, sig, sig1, med, dpix; + int64_t i, n, lcut, hcut, nlevelsm1, pix; /* Leave here if the mesh is already classified as `bad' */ - if (bkg->mean<=-BIG) - { - *mean = *sigma = -BIG; - return -BIG; - } + if (bkg->mean <= -BIG) { + *mean = *sigma = -BIG; + return -BIG; + } histo = bkg->histo; - hcut = nlevelsm1 = bkg->nlevels-1; + hcut = nlevelsm1 = bkg->nlevels - 1; lcut = 0; - sig = 10.0*nlevelsm1; + sig = 10.0 * nlevelsm1; sig1 = 1.0; mea = med = bkg->mean; /* iterate until sigma converges or drops below 0.1 (up to 100 iterations) */ - for (n=100; n-- && (sig>=0.1) && (fabs(sig/sig1-1.0)>EPS);) - { - sig1 = sig; - sum = mea = sig = 0.0; - lowsum = highsum = 0; - histot = hilow = histo+lcut; - hihigh = histo+hcut; - - for (i=lcut; i<=hcut; i++) - { - if (lowsum=histo?((hihigh-histo) + 0.5 + - ((double)highsum-lowsum)/(2.0*(*hilow>*hihigh? - *hilow:*hihigh))) - : 0.0; - if (sum) - { - mea /= (double)sum; - sig = sig/sum - mea*mea; - } - - sig = sig>0.0?sqrt(sig):0.0; - lcut = (ftemp=med-3.0*sig)>0.0 ?(int64_t)(ftemp>0.0?ftemp+0.5:ftemp-0.5):0; - hcut = (ftemp=med+3.0*sig)0.0?ftemp+0.5:ftemp-0.5) - : nlevelsm1; + for (n = 100; n-- && (sig >= 0.1) && (fabs(sig / sig1 - 1.0) > EPS);) { + sig1 = sig; + sum = mea = sig = 0.0; + lowsum = highsum = 0; + histot = hilow = histo + lcut; + hihigh = histo + hcut; + + for (i = lcut; i <= hcut; i++) { + if (lowsum < highsum) { + lowsum += *(hilow++); + } else { + highsum += *(hihigh--); + } + sum += (pix = *(histot++)); + mea += (dpix = (double)pix * i); + sig += dpix * i; + } + med = hihigh >= histo + ? ((hihigh - histo) + 0.5 + + ((double)highsum - lowsum) + / (2.0 * (*hilow > *hihigh ? *hilow : *hihigh))) + : 0.0; + if (sum) { + mea /= (double)sum; + sig = sig / sum - mea * mea; } - *mean = fabs(sig)>0.0? (fabs(bkg->sigma/(sig*bkg->qscale)-1) < 0.0 ? - bkg->qzero+mea*bkg->qscale - :(fabs((mea-med)/sig)< 0.3 ? - bkg->qzero+(2.5*med-1.5*mea)*bkg->qscale - :bkg->qzero+med*bkg->qscale)) - :bkg->qzero+mea*bkg->qscale; - *sigma = sig*bkg->qscale; + sig = sig > 0.0 ? sqrt(sig) : 0.0; + lcut = (ftemp = med - 3.0 * sig) > 0.0 + ? (int64_t)(ftemp > 0.0 ? ftemp + 0.5 : ftemp - 0.5) + : 0; + hcut = (ftemp = med + 3.0 * sig) < nlevelsm1 + ? (int64_t)(ftemp > 0.0 ? ftemp + 0.5 : ftemp - 0.5) + : nlevelsm1; + } + *mean = fabs(sig) > 0.0 + ? (fabs(bkg->sigma / (sig * bkg->qscale) - 1) < 0.0 + ? bkg->qzero + mea * bkg->qscale + : (fabs((mea - med) / sig) < 0.3 + ? bkg->qzero + (2.5 * med - 1.5 * mea) * bkg->qscale + : bkg->qzero + med * bkg->qscale)) + : bkg->qzero + mea * bkg->qscale; + + *sigma = sig * bkg->qscale; return *mean; } /****************************************************************************/ -int filterback(sep_bkg *bkg, int64_t fw, int64_t fh, double fthresh) +int filterback(sep_bkg * bkg, int64_t fw, int64_t fh, double fthresh) /* Median filterthe background map to remove the contribution * from bright sources. */ { - float *back, *sigma, *back2, *sigma2, *bmask, *smask, *sigmat; + float *back, *sigma, *back2, *sigma2, *bmask, *smask, *sigmat; float d2, d2min, med, val, sval; int64_t i, j, px, py, np, nx, ny, npx, npx2, npy, npy2, dpx, dpy, x, y, nmin; int status; @@ -516,120 +553,125 @@ int filterback(sep_bkg *bkg, int64_t fw, int64_t fh, double fthresh) nx = bkg->nx; ny = bkg->ny; np = bkg->n; - npx = fw/2; - npy = fh/2; + npx = fw / 2; + npy = fh / 2; npy *= nx; - QMALLOC(bmask, float, (2*npx+1)*(2*npy+1), status); - QMALLOC(smask, float, (2*npx+1)*(2*npy+1), status); + QMALLOC(bmask, float, (2 * npx + 1) * (2 * npy + 1), status); + QMALLOC(smask, float, (2 * npx + 1) * (2 * npy + 1), status); QMALLOC(back2, float, np, status); QMALLOC(sigma2, float, np, status); back = bkg->back; sigma = bkg->sigma; - val = sval = 0.0; /* to avoid gcc -Wall warnings */ - -/* Look for `bad' meshes and interpolate them if necessary */ - for (i=0,py=0; py-BIG) - { - d2 = (float)(x-px)*(x-px)+(y-py)*(y-py); - if (d2 -BIG) { + d2 = (float)(x - px) * (x - px) + (y - py) * (y - py); + if (d2 < d2min) { val = back[j]; sval = sigma[j]; nmin = 1; d2min = d2; - } - else if (d2==d2min) - { + } else if (d2 == d2min) { val += back[j]; sval += sigma[j]; nmin++; - } } - back2[i] = nmin? val/nmin: 0.0; - sigma[i] = nmin? sval/nmin: 1.0; + } + } } - memcpy(back, back2, (size_t)np*sizeof(float)); + back2[i] = nmin ? val / nmin : 0.0; + sigma[i] = nmin ? sval / nmin : 1.0; + } + } + } + memcpy(back, back2, (size_t)np * sizeof(float)); -/* Do the actual filtering */ - for (py=0; pynpy) + if (npy2 > npy) { npy2 = npy; - if (npy2>py) + } + if (npy2 > py) { npy2 = py; - for (px=0; pxnpx) + if (npx2 > npx) { npx2 = npx; - if (npx2>px) + } + if (npx2 > px) { npx2 = px; - i=0; - for (dpy = -npy2; dpy<=npy2; dpy+=nx) - { - y = py+dpy; - for (dpx = -npx2; dpx <= npx2; dpx++) - { - x = px+dpx; - bmask[i] = back[x+y]; - smask[i++] = sigma[x+y]; - } - } - if (fabs((med=fqmedian(bmask, i))-back[px+py])>=fthresh) - { - back2[px+py] = med; - sigma2[px+py] = fqmedian(smask, i); - } - else - { - back2[px+py] = back[px+py]; - sigma2[px+py] = sigma[px+py]; + } + i = 0; + for (dpy = -npy2; dpy <= npy2; dpy += nx) { + y = py + dpy; + for (dpx = -npx2; dpx <= npx2; dpx++) { + x = px + dpx; + bmask[i] = back[x + y]; + smask[i++] = sigma[x + y]; } } + if (fabs((med = fqmedian(bmask, i)) - back[px + py]) >= fthresh) { + back2[px + py] = med; + sigma2[px + py] = fqmedian(smask, i); + } else { + back2[px + py] = back[px + py]; + sigma2[px + py] = sigma[px + py]; + } } + } free(bmask); free(smask); bmask = smask = NULL; - memcpy(back, back2, np*sizeof(float)); + memcpy(back, back2, np * sizeof(float)); bkg->global = fqmedian(back2, np); free(back2); back2 = NULL; - memcpy(sigma, sigma2, np*sizeof(float)); + memcpy(sigma, sigma2, np * sizeof(float)); bkg->globalrms = fqmedian(sigma2, np); - if (bkg->globalrms <= 0.0) - { - sigmat = sigma2+np; - for (i=np; i-- && *(--sigmat)>0.0;); - if (i>=0 && i<(np-1)) - bkg->globalrms = fqmedian(sigmat+1, np-1-i); - else + if (bkg->globalrms <= 0.0) { + sigmat = sigma2 + np; + for (i = np; i-- && *(--sigmat) > 0.0;) + ; + if (i >= 0 && i < (np - 1)) { + bkg->globalrms = fqmedian(sigmat + 1, np - 1 - i); + } else { bkg->globalrms = 1.0; } + } free(sigma2); sigma2 = NULL; return status; - exit: - if (bmask) free(bmask); - if (smask) free(smask); - if (back2) free(back2); - if (sigma2) free(sigma2); +exit: + if (bmask) { + free(bmask); + } + if (smask) { + free(smask); + } + if (back2) { + free(back2); + } + if (sigma2) { + free(sigma2); + } return status; } @@ -638,9 +680,8 @@ int filterback(sep_bkg *bkg, int64_t fw, int64_t fh, double fthresh) /* * Pre-compute 2nd derivatives along the y direction at background nodes. */ -int makebackspline(const sep_bkg *bkg, float *map, float *dmap) -{ - int64_t x, y, nbx, nby, nbym1; +int makebackspline(const sep_bkg * bkg, float * map, float * dmap) { + int64_t x, y, nbx, nby, nbym1; int status; float *dmapt, *mapt, *u, temp; u = NULL; @@ -649,125 +690,116 @@ int makebackspline(const sep_bkg *bkg, float *map, float *dmap) nbx = bkg->nx; nby = bkg->ny; nbym1 = nby - 1; - for (x=0; x1) - { - QMALLOC(u, float, nbym1, status); /* temporary array */ - *dmapt = *u = 0.0; /* "natural" lower boundary condition */ - mapt += nbx; - for (y=1; y 1) { + QMALLOC(u, float, nbym1, status); /* temporary array */ + *dmapt = *u = 0.0; /* "natural" lower boundary condition */ + mapt += nbx; + for (y = 1; y < nbym1; y++, mapt += nbx) { + temp = -1 / (*dmapt + 4); + *(dmapt += nbx) = temp; + temp *= *(u++) - 6 * (*(mapt + nbx) + *(mapt - nbx) - 2 * *mapt); + *u = temp; + } + *(dmapt += nbx) = 0.0; /* "natural" upper boundary condition */ + for (y = nby - 2; y--;) { + temp = *dmapt; + dmapt -= nbx; + *dmapt = (*dmapt * temp + *(u--)) / 6.0; + } + free(u); + u = NULL; + } else { + *dmapt = 0.0; } + } return status; - exit: - if (u) free(u); +exit: + if (u) { + free(u); + } return status; } /*****************************************************************************/ -float sep_bkg_global(const sep_bkg *bkg) -{ +float sep_bkg_global(const sep_bkg * bkg) { return bkg->global; } -float sep_bkg_globalrms(const sep_bkg *bkg) -{ +float sep_bkg_globalrms(const sep_bkg * bkg) { return bkg->globalrms; } /*****************************************************************************/ -float sep_bkg_pix(const sep_bkg *bkg, int64_t x, int64_t y) +float sep_bkg_pix(const sep_bkg * bkg, int64_t x, int64_t y) /* * return background at position x,y. * (linear interpolation between background map vertices). */ { - int64_t nx, ny, xl, yl, pos; + int64_t nx, ny, xl, yl, pos; double dx, dy, cdx; - float *b; - float b0, b1, b2, b3; + float * b; + float b0, b1, b2, b3; b = bkg->back; nx = bkg->nx; ny = bkg->ny; - dx = (double)x/bkg->bw - 0.5; - dy = (double)y/bkg->bh - 0.5; + dx = (double)x / bkg->bw - 0.5; + dy = (double)y / bkg->bh - 0.5; dx -= (xl = (int64_t)dx); dy -= (yl = (int64_t)dy); - if (xl<0) - { + if (xl < 0) { xl = 0; dx -= 1.0; - } - else if (xl>=nx-1) - { - xl = nx<2 ? 0 : nx-2; + } else if (xl >= nx - 1) { + xl = nx < 2 ? 0 : nx - 2; dx += 1.0; - } + } - if (yl<0) - { + if (yl < 0) { yl = 0; dy -= 1.0; - } - else if (yl>=ny-1) - { - yl = ny<2 ? 0 : ny-2; + } else if (yl >= ny - 1) { + yl = ny < 2 ? 0 : ny - 2; dy += 1.0; - } + } - pos = yl*nx + xl; + pos = yl * nx + xl; cdx = 1 - dx; - b0 = *(b+=pos); /* consider when nbackx or nbacky = 1 */ - b1 = nx<2? b0:*(++b); - b2 = ny<2? *b:*(b+=nx); - b3 = nx<2? *b:*(--b); + b0 = *(b += pos); /* consider when nbackx or nbacky = 1 */ + b1 = nx < 2 ? b0 : *(++b); + b2 = ny < 2 ? *b : *(b += nx); + b3 = nx < 2 ? *b : *(--b); - return (float)((1-dy)*(cdx*b0 + dx*b1) + dy*(dx*b2 + cdx*b3)); + return (float)((1 - dy) * (cdx * b0 + dx * b1) + dy * (dx * b2 + cdx * b3)); } /*****************************************************************************/ -int bkg_line_flt_internal(const sep_bkg *bkg, float *values, float *dvalues, int64_t y, - float *line) +int bkg_line_flt_internal( + const sep_bkg * bkg, float * values, float * dvalues, int64_t y, float * line +) /* Interpolate background at line y (bicubic spline interpolation between * background map vertices) and save to line. * (values, dvalues) is either (bkg->back, bkg->dback) or * (bkg->sigma, bkg->dsigma) depending on whether the background value or rms * is being evaluated. */ { - int64_t i,j,x,yl, nbx,nbxm1,nby, nx,width, ystep, changepoint; + int64_t i, j, x, yl, nbx, nbxm1, nby, nx, width, ystep, changepoint; int status; - float dx,dx0,dy,dy3, cdx,cdy,cdy3, temp, xstep; + float dx, dx0, dy, dy3, cdx, cdy, cdy3, temp, xstep; float *nodebuf, *dnodebuf, *u; float *node, *nodep, *dnode, *blo, *bhi, *dblo, *dbhi; @@ -780,116 +812,108 @@ int bkg_line_flt_internal(const sep_bkg *bkg, float *values, float *dvalues, int nbx = bkg->nx; nbxm1 = nbx - 1; nby = bkg->ny; - if (nby > 1) - { - dy = (float)y/bkg->bh - 0.5; - dy -= (yl = (int64_t)dy); - if (yl<0) - { - yl = 0; - dy -= 1.0; - } - else if (yl>=nby-1) - { - yl = nby<2 ? 0 : nby-2; - dy += 1.0; - } - /*-- Interpolation along y for each node */ - cdy = 1 - dy; - dy3 = (dy*dy*dy-dy); - cdy3 = (cdy*cdy*cdy-cdy); - ystep = nbx*yl; - blo = values + ystep; - bhi = blo + nbx; - dblo = dvalues + ystep; - dbhi = dblo + nbx; - QMALLOC(nodebuf, float, nbx, status); /* Interpolated background */ - nodep = node = nodebuf; - for (x=nbx; x--;) - *(nodep++) = cdy**(blo++) + dy**(bhi++) + cdy3**(dblo++) + - dy3**(dbhi++); - - /*-- Computation of 2nd derivatives along x */ - QMALLOC(dnodebuf, float, nbx, status); /* 2nd derivative along x */ - dnode = dnodebuf; - if (nbx>1) - { - QMALLOC(u, float, nbxm1, status); /* temporary array */ - *dnode = *u = 0.0; /* "natural" lower boundary condition */ - nodep = node+1; - for (x=nbxm1; --x; nodep++) - { - temp = -1/(*(dnode++)+4); - *dnode = temp; - temp *= *(u++) - 6*(*(nodep+1)+*(nodep-1)-2**nodep); - *u = temp; - } - *(++dnode) = 0.0; /* "natural" upper boundary condition */ - for (x=nbx-2; x--;) - { - temp = *(dnode--); - *dnode = (*dnode*temp+*(u--))/6.0; - } - free(u); - u = NULL; - dnode--; - } + if (nby > 1) { + dy = (float)y / bkg->bh - 0.5; + dy -= (yl = (int64_t)dy); + if (yl < 0) { + yl = 0; + dy -= 1.0; + } else if (yl >= nby - 1) { + yl = nby < 2 ? 0 : nby - 2; + dy += 1.0; } - else - { - /*-- No interpolation and no new 2nd derivatives needed along y */ - node = values; - dnode = dvalues; + /*-- Interpolation along y for each node */ + cdy = 1 - dy; + dy3 = (dy * dy * dy - dy); + cdy3 = (cdy * cdy * cdy - cdy); + ystep = nbx * yl; + blo = values + ystep; + bhi = blo + nbx; + dblo = dvalues + ystep; + dbhi = dblo + nbx; + QMALLOC(nodebuf, float, nbx, status); /* Interpolated background */ + nodep = node = nodebuf; + for (x = nbx; x--;) { + *(nodep++) = cdy * *(blo++) + dy * *(bhi++) + cdy3 * *(dblo++) + dy3 * *(dbhi++); } - /*-- Interpolation along x */ - if (nbx>1) - { - nx = bkg->bw; - xstep = 1.0/nx; - changepoint = nx/2; - dx = (xstep - 1)/2; /* dx of the first pixel in the row */ - dx0 = ((nx+1)%2)*xstep/2; /* dx of the 1st pixel right to a bkgnd node */ - blo = node; - bhi = node + 1; - dblo = dnode; - dbhi = dnode + 1; - for (x=i=0,j=width; j--; i++, dx += xstep) - { - if (i==changepoint && x>0 && x 1) { + QMALLOC(u, float, nbxm1, status); /* temporary array */ + *dnode = *u = 0.0; /* "natural" lower boundary condition */ + nodep = node + 1; + for (x = nbxm1; --x; nodep++) { + temp = -1 / (*(dnode++) + 4); + *dnode = temp; + temp *= *(u++) - 6 * (*(nodep + 1) + *(nodep - 1) - 2 * *nodep); + *u = temp; + } + *(++dnode) = 0.0; /* "natural" upper boundary condition */ + for (x = nbx - 2; x--;) { + temp = *(dnode--); + *dnode = (*dnode * temp + *(u--)) / 6.0; + } + free(u); + u = NULL; + dnode--; } - else - for (j=width; j--;) - { - *(line++) = (float)*node; + } else { + /*-- No interpolation and no new 2nd derivatives needed along y */ + node = values; + dnode = dvalues; + } + + /*-- Interpolation along x */ + if (nbx > 1) { + nx = bkg->bw; + xstep = 1.0 / nx; + changepoint = nx / 2; + dx = (xstep - 1) / 2; /* dx of the first pixel in the row */ + dx0 = ((nx + 1) % 2) * xstep / 2; /* dx of the 1st pixel right to a bkgnd node */ + blo = node; + bhi = node + 1; + dblo = dnode; + dbhi = dnode + 1; + for (x = i = 0, j = width; j--; i++, dx += xstep) { + if (i == changepoint && x > 0 && x < nbxm1) { + blo++; + bhi++; + dblo++; + dbhi++; + dx = dx0; } + cdx = 1 - dx; + + *(line++) = (float)(cdx * (*blo + (cdx * cdx - 1) * *dblo) + + dx * (*bhi + (dx * dx - 1) * *dbhi)); + + if (i == nx) { + x++; + i = 0; + } + } + } else { + for (j = width; j--;) { + *(line++) = (float)*node; + } + } - exit: - if (nodebuf) free(nodebuf); - if (dnodebuf) free(dnodebuf); - if (u) free(u); +exit: + if (nodebuf) { + free(nodebuf); + } + if (dnodebuf) { + free(dnodebuf); + } + if (u) { + free(u); + } return status; } -int sep_bkg_line_flt(const sep_bkg *bkg, int64_t y, float *line) +int sep_bkg_line_flt(const sep_bkg * bkg, int64_t y, float * line) /* Interpolate background at line y (bicubic spline interpolation between * background map vertices) and save to line */ { @@ -898,7 +922,7 @@ int sep_bkg_line_flt(const sep_bkg *bkg, int64_t y, float *line) /*****************************************************************************/ -int sep_bkg_rmsline_flt(const sep_bkg *bkg, int64_t y, float *line) +int sep_bkg_rmsline_flt(const sep_bkg * bkg, int64_t y, float * line) /* Interpolate background rms at line y (bicubic spline interpolation between * background map vertices) and save to line */ { @@ -909,148 +933,153 @@ int sep_bkg_rmsline_flt(const sep_bkg *bkg, int64_t y, float *line) /* Multiple dtype functions and convenience functions. * These mostly wrap the two "line" functions above. */ -int sep_bkg_line(const sep_bkg *bkg, int64_t y, void *line, int dtype) -{ +int sep_bkg_line(const sep_bkg * bkg, int64_t y, void * line, int dtype) { array_writer write_array; int64_t size; int status; - float *tmpline; + float * tmpline; - if (dtype == SEP_TFLOAT) + if (dtype == SEP_TFLOAT) { return sep_bkg_line_flt(bkg, y, (float *)line); + } tmpline = NULL; status = get_array_writer(dtype, &write_array, &size); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } QMALLOC(tmpline, float, bkg->w, status); status = sep_bkg_line_flt(bkg, y, tmpline); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } /* write to desired output type */ write_array(tmpline, bkg->w, line); - exit: +exit: free(tmpline); return status; } -int sep_bkg_rmsline(const sep_bkg *bkg, int64_t y, void *line, int dtype) -{ +int sep_bkg_rmsline(const sep_bkg * bkg, int64_t y, void * line, int dtype) { array_writer write_array; int64_t size; int status; - float *tmpline; + float * tmpline; - if (dtype == SEP_TFLOAT) + if (dtype == SEP_TFLOAT) { return sep_bkg_rmsline_flt(bkg, y, (float *)line); + } tmpline = NULL; status = get_array_writer(dtype, &write_array, &size); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } QMALLOC(tmpline, float, bkg->w, status); status = sep_bkg_rmsline_flt(bkg, y, tmpline); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } /* write to desired output type */ write_array(tmpline, bkg->w, line); - exit: +exit: free(tmpline); return status; } -int sep_bkg_array(const sep_bkg *bkg, void *arr, int dtype) -{ +int sep_bkg_array(const sep_bkg * bkg, void * arr, int dtype) { int64_t y, width, size; int status; array_writer write_array; - float *tmpline; - BYTE *line; + float * tmpline; + BYTE * line; tmpline = NULL; status = RETURN_OK; width = bkg->w; - if (dtype == SEP_TFLOAT) - { - tmpline = (float *)arr; - for (y=0; yh; y++, tmpline+=width) - if ((status = sep_bkg_line_flt(bkg, y, tmpline)) != RETURN_OK) - return status; - return status; + if (dtype == SEP_TFLOAT) { + tmpline = (float *)arr; + for (y = 0; y < bkg->h; y++, tmpline += width) { + if ((status = sep_bkg_line_flt(bkg, y, tmpline)) != RETURN_OK) { + return status; + } } + return status; + } - if ((status = get_array_writer(dtype, &write_array, &size)) != RETURN_OK) + if ((status = get_array_writer(dtype, &write_array, &size)) != RETURN_OK) { goto exit; + } QMALLOC(tmpline, float, width, status); line = (BYTE *)arr; - for (y=0; yh; y++, line += size*width) - { - if ((status = sep_bkg_line_flt(bkg, y, tmpline)) != RETURN_OK) - goto exit; - write_array(tmpline, width, line); + for (y = 0; y < bkg->h; y++, line += size * width) { + if ((status = sep_bkg_line_flt(bkg, y, tmpline)) != RETURN_OK) { + goto exit; } + write_array(tmpline, width, line); + } - exit: +exit: free(tmpline); return status; } -int sep_bkg_rmsarray(const sep_bkg *bkg, void *arr, int dtype) -{ +int sep_bkg_rmsarray(const sep_bkg * bkg, void * arr, int dtype) { int64_t y, width, size; int status; array_writer write_array; - float *tmpline; - BYTE *line; + float * tmpline; + BYTE * line; tmpline = NULL; status = RETURN_OK; width = bkg->w; - if (dtype == SEP_TFLOAT) - { - tmpline = (float *)arr; - for (y=0; yh; y++, tmpline+=width) - if ((status = sep_bkg_rmsline_flt(bkg, y, tmpline)) != RETURN_OK) - return status; - return status; + if (dtype == SEP_TFLOAT) { + tmpline = (float *)arr; + for (y = 0; y < bkg->h; y++, tmpline += width) { + if ((status = sep_bkg_rmsline_flt(bkg, y, tmpline)) != RETURN_OK) { + return status; + } } + return status; + } - if ((status = get_array_writer(dtype, &write_array, &size)) != RETURN_OK) + if ((status = get_array_writer(dtype, &write_array, &size)) != RETURN_OK) { goto exit; + } QMALLOC(tmpline, float, width, status); line = (BYTE *)arr; - for (y=0; yh; y++, line += size*width) - { - if ((status = sep_bkg_rmsline_flt(bkg, y, tmpline)) != RETURN_OK) - goto exit; - write_array(tmpline, width, line); + for (y = 0; y < bkg->h; y++, line += size * width) { + if ((status = sep_bkg_rmsline_flt(bkg, y, tmpline)) != RETURN_OK) { + goto exit; } + write_array(tmpline, width, line); + } - exit: +exit: free(tmpline); return status; } -int sep_bkg_subline(const sep_bkg *bkg, int64_t y, void *line, int dtype) -{ +int sep_bkg_subline(const sep_bkg * bkg, int64_t y, void * line, int dtype) { array_writer subtract_array; int status; int64_t size; - PIXTYPE *tmpline; + PIXTYPE * tmpline; tmpline = NULL; status = RETURN_OK; @@ -1058,27 +1087,28 @@ int sep_bkg_subline(const sep_bkg *bkg, int64_t y, void *line, int dtype) QMALLOC(tmpline, PIXTYPE, bkg->w, status); status = sep_bkg_line_flt(bkg, y, tmpline); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } status = get_array_subtractor(dtype, &subtract_array, &size); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } subtract_array(tmpline, bkg->w, line); - exit: +exit: free(tmpline); return status; } -int sep_bkg_subarray(const sep_bkg *bkg, void *arr, int dtype) -{ +int sep_bkg_subarray(const sep_bkg * bkg, void * arr, int dtype) { array_writer subtract_array; int64_t y, size, width; int status; - PIXTYPE *tmpline; - BYTE *arrt; + PIXTYPE * tmpline; + BYTE * arrt; tmpline = NULL; status = RETURN_OK; @@ -1088,31 +1118,30 @@ int sep_bkg_subarray(const sep_bkg *bkg, void *arr, int dtype) QMALLOC(tmpline, PIXTYPE, width, status); status = get_array_subtractor(dtype, &subtract_array, &size); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } - for (y=0; yh; y++, arrt+=(width*size)) - { - if ((status = sep_bkg_line_flt(bkg, y, tmpline)) != RETURN_OK) - goto exit; - subtract_array(tmpline, width, arrt); + for (y = 0; y < bkg->h; y++, arrt += (width * size)) { + if ((status = sep_bkg_line_flt(bkg, y, tmpline)) != RETURN_OK) { + goto exit; } + subtract_array(tmpline, width, arrt); + } - exit: +exit: free(tmpline); return status; } /*****************************************************************************/ -void sep_bkg_free(sep_bkg *bkg) -{ - if (bkg) - { - free(bkg->back); - free(bkg->dback); - free(bkg->sigma); - free(bkg->dsigma); - } +void sep_bkg_free(sep_bkg * bkg) { + if (bkg) { + free(bkg->back); + free(bkg->dback); + free(bkg->sigma); + free(bkg->dsigma); + } free(bkg); } diff --git a/src/convolve.c b/src/convolve.c index 8e070ae..306ea24 100644 --- a/src/convolve.c +++ b/src/convolve.c @@ -20,12 +20,13 @@ f* GNU Lesser General Public License for more details. * *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#include #include #include -#include + +#include "extract.h" #include "sep.h" #include "sepcore.h" -#include "extract.h" /* Convolve one line of an image with a given kernel. * @@ -35,65 +36,68 @@ f* GNU Lesser General Public License for more details. * convw, convh : width and height of conv * buf : output convolved line (buf->dw elements long) */ -int convolve(arraybuffer *buf, int64_t y, const float *conv, - int64_t convw, int64_t convh, PIXTYPE *out) -{ +int convolve( + arraybuffer * buf, + int64_t y, + const float * conv, + int64_t convw, + int64_t convh, + PIXTYPE * out +) { int64_t convw2, convn, cx, cy, i, dcx, y0; - PIXTYPE *line; /* current line in input buffer */ - PIXTYPE *outend; /* end of output buffer */ + PIXTYPE * line; /* current line in input buffer */ + PIXTYPE * outend; /* end of output buffer */ PIXTYPE *src, *dst, *dstend; outend = out + buf->dw; - convw2 = convw/2; - y0 = y - convh/2; /* start line in image */ + convw2 = convw / 2; + y0 = y - convh / 2; /* start line in image */ /* Cut off top of kernel if it extends beyond image */ - if (y0 + convh > buf->dh) + if (y0 + convh > buf->dh) { convh = buf->dh - y0; + } /* cut off bottom of kernel if it extends beyond image */ - if (y0 < 0) - { - convh = convh + y0; - conv += convw*(-y0); - y0 = 0; - } + if (y0 < 0) { + convh = convh + y0; + conv += convw * (-y0); + y0 = 0; + } /* check that buffer has needed lines */ - if ((y0 < buf->yoff) || (y0+convh > buf->yoff + buf->bh)) + if ((y0 < buf->yoff) || (y0 + convh > buf->yoff + buf->bh)) { return LINE_NOT_IN_BUF; + } - memset(out, 0, buf->dw*sizeof(PIXTYPE)); /* initialize output to zero */ + memset(out, 0, buf->dw * sizeof(PIXTYPE)); /* initialize output to zero */ /* loop over pixels in the convolution kernel */ convn = convw * convh; - for (i=0; ibptr + buf->bw * (y0 - buf->yoff + cy); /* start of line */ - - /* get start and end positions in the source and target line */ - dcx = cx - convw2; /* offset of conv pixel from conv center; - determines offset between in and out line */ - if (dcx >= 0) - { - src = line + dcx; - dst = out; - dstend = outend - dcx; - } - else - { - src = line; - dst = out - dcx; - dstend = outend; - } - - /* multiply and add the values */ - while (dst < dstend) - *(dst++) += conv[i] * *(src++); + for (i = 0; i < convn; i++) { + cx = i % convw; /* x index in conv kernel */ + cy = i / convw; /* y index in conv kernel */ + line = buf->bptr + buf->bw * (y0 - buf->yoff + cy); /* start of line */ + + /* get start and end positions in the source and target line */ + dcx = cx - convw2; /* offset of conv pixel from conv center; + determines offset between in and out line */ + if (dcx >= 0) { + src = line + dcx; + dst = out; + dstend = outend - dcx; + } else { + src = line; + dst = out - dcx; + dstend = outend; } + /* multiply and add the values */ + while (dst < dstend) { + *(dst++) += conv[i] * *(src++); + } + } + return RETURN_OK; } @@ -119,95 +123,100 @@ int convolve(arraybuffer *buf, int64_t y, const float *conv, * imbuf and nbuf should have same data dimensions and be on the same line * (their `yoff` fields should be the same). */ -int matched_filter(arraybuffer *imbuf, arraybuffer *nbuf, int64_t y, - const float *conv, int64_t convw, int64_t convh, - PIXTYPE *work, PIXTYPE *out, int noise_type) -{ +int matched_filter( + arraybuffer * imbuf, + arraybuffer * nbuf, + int64_t y, + const float * conv, + int64_t convw, + int64_t convh, + PIXTYPE * work, + PIXTYPE * out, + int noise_type +) { int64_t convw2, convn, cx, cy, i, dcx, y0; PIXTYPE imval, varval; - PIXTYPE *imline, *nline; /* current line in input buffer */ - PIXTYPE *outend; /* end of output buffer */ + PIXTYPE *imline, *nline; /* current line in input buffer */ + PIXTYPE * outend; /* end of output buffer */ PIXTYPE *src_im, *src_n, *dst_num, *dst_denom, *dst_num_end; outend = out + imbuf->dw; - convw2 = convw/2; - y0 = y - convh/2; /* start line in image */ + convw2 = convw / 2; + y0 = y - convh / 2; /* start line in image */ /* Cut off top of kernel if it extends beyond image */ - if (y0 + convh > imbuf->dh) + if (y0 + convh > imbuf->dh) { convh = imbuf->dh - y0; + } /* cut off bottom of kernel if it extends beyond image */ - if (y0 < 0) - { - convh = convh + y0; - conv += convw*(-y0); - y0 = 0; - } + if (y0 < 0) { + convh = convh + y0; + conv += convw * (-y0); + y0 = 0; + } /* check that buffer has needed lines */ - if ((y0 < imbuf->yoff) || (y0+convh > imbuf->yoff + imbuf->bh) || - (y0 < nbuf->yoff) || (y0+convh > nbuf->yoff + nbuf->bh)) + if ((y0 < imbuf->yoff) || (y0 + convh > imbuf->yoff + imbuf->bh) || (y0 < nbuf->yoff) + || (y0 + convh > nbuf->yoff + nbuf->bh)) + { return LINE_NOT_IN_BUF; + } /* check that image and noise buffer match */ - if ((imbuf->yoff != nbuf->yoff) || (imbuf->dw != nbuf->dw)) - return LINE_NOT_IN_BUF; /* TODO new error status code */ + if ((imbuf->yoff != nbuf->yoff) || (imbuf->dw != nbuf->dw)) { + return LINE_NOT_IN_BUF; /* TODO new error status code */ + } /* initialize output buffers to zero */ - memset(out, 0, imbuf->bw*sizeof(PIXTYPE)); - memset(work, 0, imbuf->bw*sizeof(PIXTYPE)); + memset(out, 0, imbuf->bw * sizeof(PIXTYPE)); + memset(work, 0, imbuf->bw * sizeof(PIXTYPE)); /* loop over pixels in the convolution kernel */ convn = convw * convh; - for (i=0; ibptr + imbuf->bw * (y0 - imbuf->yoff + cy); - nline = nbuf->bptr + nbuf->bw * (y0 - nbuf->yoff + cy); - - /* get start and end positions in the source and target line */ - dcx = cx - convw2; /* offset of conv pixel from conv center; - determines offset between in and out line */ - if (dcx >= 0) - { - src_im = imline + dcx; - src_n = nline + dcx; - dst_num = out; - dst_denom = work; - dst_num_end = outend - dcx; - } - else - { - src_im = imline; - src_n = nline; - dst_num = out - dcx; - dst_denom = work - dcx; - dst_num_end = outend; - } - - /* actually calculate values */ - while (dst_num < dst_num_end) - { - imval = *src_im; - varval = (noise_type == SEP_NOISE_VAR)? (*src_n): (*src_n)*(*src_n); - if (varval != 0.0) - { - *dst_num += conv[i] * imval / varval; - *dst_denom += conv[i] * conv[i] / varval; - } - src_im++; - src_n++; - dst_num++; - dst_denom++; - } - } /* close loop over convolution kernel */ + for (i = 0; i < convn; i++) { + cx = i % convw; /* x index in conv kernel */ + cy = i / convw; /* y index in conv kernel */ + imline = imbuf->bptr + imbuf->bw * (y0 - imbuf->yoff + cy); + nline = nbuf->bptr + nbuf->bw * (y0 - nbuf->yoff + cy); + + /* get start and end positions in the source and target line */ + dcx = cx - convw2; /* offset of conv pixel from conv center; + determines offset between in and out line */ + if (dcx >= 0) { + src_im = imline + dcx; + src_n = nline + dcx; + dst_num = out; + dst_denom = work; + dst_num_end = outend - dcx; + } else { + src_im = imline; + src_n = nline; + dst_num = out - dcx; + dst_denom = work - dcx; + dst_num_end = outend; + } + + /* actually calculate values */ + while (dst_num < dst_num_end) { + imval = *src_im; + varval = (noise_type == SEP_NOISE_VAR) ? (*src_n) : (*src_n) * (*src_n); + if (varval != 0.0) { + *dst_num += conv[i] * imval / varval; + *dst_denom += conv[i] * conv[i] / varval; + } + src_im++; + src_n++; + dst_num++; + dst_denom++; + } + } /* close loop over convolution kernel */ /* take the square root of the denominator (work) buffer and divide the * numerator by it. */ - for (dst_num=out, dst_denom=work; dst_num < outend; dst_num++, dst_denom++) - *dst_num = *dst_num / sqrt(*dst_denom); + for (dst_num = out, dst_denom = work; dst_num < outend; dst_num++, dst_denom++) { + *dst_num = *dst_num / sqrt(*dst_denom); + } return RETURN_OK; } diff --git a/src/deblend.c b/src/deblend.c index 24b9339..05fe1aa 100644 --- a/src/deblend.c +++ b/src/deblend.c @@ -1,55 +1,55 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include #include #include #include #include + +#include "extract.h" #include "sep.h" #include "sepcore.h" -#include "extract.h" -#ifndef RAND_MAX -#define RAND_MAX 2147483647 +#ifndef RAND_MAX +#define RAND_MAX 2147483647 #endif -#define NBRANCH 16 /* starting number per branch */ +#define NBRANCH 16 /* starting number per branch */ -static _Atomic int nsonmax = 1024; /* max. number sub-objects per level */ +static _Atomic int nsonmax = 1024; /* max. number sub-objects per level */ /* get and set pixstack */ -void sep_set_sub_object_limit(int val) -{ +void sep_set_sub_object_limit(int val) { nsonmax = val; } -int sep_get_sub_object_limit() -{ +int sep_get_sub_object_limit() { return nsonmax; } int belong(int, objliststruct *, int, objliststruct *); -int64_t *createsubmap(objliststruct *, int64_t, int64_t *, int64_t *, int64_t *, int64_t *); +int64_t * +createsubmap(objliststruct *, int64_t, int64_t *, int64_t *, int64_t *, int64_t *); int gatherup(objliststruct *, objliststruct *); /******************************** deblend ************************************/ @@ -60,16 +60,20 @@ NOTE: Even if the object is not deblended, the output objlist threshold is This can return two error codes: DEBLEND_OVERFLOW or MEMORY_ALLOC_ERROR */ -int deblend(objliststruct *objlistin, objliststruct *objlistout, - int deblend_nthresh, double deblend_mincont, int minarea, - deblendctx *ctx) -{ - objstruct *obj; - objliststruct debobjlist, debobjlist2; - double thresh, thresh0, value0; - int64_t h,i,j,k,l,m,subx,suby,subh,subw,xn, nbm = NBRANCH; - int64_t *submap; - int status; +int deblend( + objliststruct * objlistin, + objliststruct * objlistout, + int deblend_nthresh, + double deblend_mincont, + int minarea, + deblendctx * ctx +) { + objstruct * obj; + objliststruct debobjlist, debobjlist2; + double thresh, thresh0, value0; + int64_t h, i, j, k, l, m, subx, suby, subh, subw, xn, nbm = NBRANCH; + int64_t * submap; + int status; submap = NULL; status = RETURN_OK; @@ -77,11 +81,11 @@ int deblend(objliststruct *objlistin, objliststruct *objlistout, l = 0; /* reset global static objlist for deblending */ - objliststruct *const objlist = ctx->objlist; - memset(objlist, 0, (size_t)xn*sizeof(objliststruct)); + objliststruct * const objlist = ctx->objlist; + memset(objlist, 0, (size_t)xn * sizeof(objliststruct)); /* initialize local object lists */ - debobjlist.obj = debobjlist2.obj = NULL; + debobjlist.obj = debobjlist2.obj = NULL; debobjlist.plist = debobjlist2.plist = NULL; debobjlist.nobj = debobjlist2.nobj = 0; debobjlist.npix = debobjlist2.npix = 0; @@ -91,118 +95,134 @@ int deblend(objliststruct *objlistin, objliststruct *objlistout, * lutz multiple times below, and we only want to create it once. */ submap = createsubmap(objlistin, l, &subx, &suby, &subw, &subh); - if (!submap) - { - status = MEMORY_ALLOC_ERROR; - goto exit; - } - - for (l=0; lnobj && status == RETURN_OK; l++) { + if (!submap) { + status = MEMORY_ALLOC_ERROR; + goto exit; + } + for (l = 0; l < objlistin->nobj && status == RETURN_OK; l++) { /* set thresholds of object lists based on object threshold */ thresh0 = objlistin->obj[l].thresh; objlistout->thresh = debobjlist2.thresh = thresh0; /* add input object to global deblending objlist and one local objlist */ - if ((status = addobjdeep(l, objlistin, &objlist[0])) != RETURN_OK) + if ((status = addobjdeep(l, objlistin, &objlist[0])) != RETURN_OK) { goto exit; - if ((status = addobjdeep(l, objlistin, &debobjlist2)) != RETURN_OK) + } + if ((status = addobjdeep(l, objlistin, &debobjlist2)) != RETURN_OK) { goto exit; + } - value0 = objlist[0].obj[0].fdflux*deblend_mincont; + value0 = objlist[0].obj[0].fdflux * deblend_mincont; ctx->ok[0] = (short)1; - for (k=1; kobj[l].fdpeak; - debobjlist.thresh = thresh > 0.0? - thresh0*pow(thresh/thresh0,(double)k/xn) : thresh0; + debobjlist.thresh = + thresh > 0.0 ? thresh0 * pow(thresh / thresh0, (double)k / xn) : thresh0; /*--------- Build tree (bottom->up) */ - if (objlist[k-1].nobj>=nsonmax) { + if (objlist[k - 1].nobj >= nsonmax) { status = DEBLEND_OVERFLOW; goto exit; } - for (i=0; iplist, submap, subx, suby, subw, - &objlist[k-1].obj[i], &debobjlist, minarea, &ctx->lutz); - if (status != RETURN_OK) + for (i = 0; i < objlist[k - 1].nobj; i++) { + status = lutz( + objlistin->plist, + submap, + subx, + suby, + subw, + &objlist[k - 1].obj[i], + &debobjlist, + minarea, + &ctx->lutz + ); + if (status != RETURN_OK) { goto exit; + } - for (j=h=0; j=nsonmax) { + m = objlist[k].nobj - 1; + if (m >= nsonmax) { status = DEBLEND_OVERFLOW; goto exit; } - if (h>=nbm-1) { - if (!(ctx->son = (short *) - realloc(ctx->son,xn*nsonmax*(nbm+=16)*sizeof(short)))) { - status = MEMORY_ALLOC_ERROR; - goto exit; + if (h >= nbm - 1) { + if (!(ctx->son = (short *) + realloc(ctx->son, xn * nsonmax * (nbm += 16) * sizeof(short)))) + { + status = MEMORY_ALLOC_ERROR; + goto exit; } } - ctx->son[k-1+xn*(i+nsonmax*(h++))] = (short)m; - ctx->ok[k+xn*m] = (short)1; - } + ctx->son[k - 1 + xn * (i + nsonmax * (h++))] = (short)m; + ctx->ok[k + xn * m] = (short)1; + } } - ctx->son[k-1+xn*(i+nsonmax*h)] = (short)-1; - } + ctx->son[k - 1 + xn * (i + nsonmax * h)] = (short)-1; + } } /*------- cut the right branches (top->down) */ - for (k = xn-2; k>=0; k--) { - obj = objlist[k+1].obj; - for (i=0; ison[k+xn*(i+nsonmax*h)])!=-1; h++) { + for (k = xn - 2; k >= 0; k--) { + obj = objlist[k + 1].obj; + for (i = 0; i < objlist[k].nobj; i++) { + for (m = h = 0; (j = (int64_t)ctx->son[k + xn * (i + nsonmax * h)]) != -1; h++) + { if (obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) { m++; } - ctx->ok[k+xn*i] &= ctx->ok[k+1+xn*j]; + ctx->ok[k + xn * i] &= ctx->ok[k + 1 + xn * j]; } - if (m>1) { - for (h=0; (j=(int64_t)ctx->son[k+xn*(i+nsonmax*h)])!=-1; h++) { - if (ctx->ok[k+1+xn*j] && - obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) { - objlist[k+1].obj[j].flag |= SEP_OBJ_MERGED; - status = addobjdeep(j, &objlist[k+1], &debobjlist2); + if (m > 1) { + for (h = 0; (j = (int64_t)ctx->son[k + xn * (i + nsonmax * h)]) != -1; h++) { + if (ctx->ok[k + 1 + xn * j] + && obj[j].fdflux - obj[j].thresh * obj[j].fdnpix > value0) + { + objlist[k + 1].obj[j].flag |= SEP_OBJ_MERGED; + status = addobjdeep(j, &objlist[k + 1], &debobjlist2); if (status != RETURN_OK) { goto exit; } } } - ctx->ok[k+xn*i] = (short)0; + ctx->ok[k + xn * i] = (short)0; } } } - if (ctx->ok[0]) + if (ctx->ok[0]) { status = addobjdeep(0, &debobjlist2, objlistout); - else + } else { status = gatherup(&debobjlist2, objlistout); + } } - exit: - if (status == DEBLEND_OVERFLOW) - put_errdetail("limit of sub-objects reached while deblending. Increase " - "it with sep.set_sub_object_limit(), decrease number of deblending " - "thresholds ,or increase the detection threshold."); +exit: + if (status == DEBLEND_OVERFLOW) { + put_errdetail( + "limit of sub-objects reached while deblending. Increase " + "it with sep.set_sub_object_limit(), decrease number of deblending " + "thresholds ,or increase the detection threshold." + ); + } free(submap); submap = NULL; free(debobjlist2.obj); free(debobjlist2.plist); - for (k=0; kson, short, deblend_nthresh*nsonmax*NBRANCH, status); - QMALLOC(ctx->ok, short, deblend_nthresh*nsonmax, status); + QMALLOC(ctx->son, short, deblend_nthresh * nsonmax * NBRANCH, status); + QMALLOC(ctx->ok, short, deblend_nthresh * nsonmax, status); QMALLOC(ctx->objlist, objliststruct, deblend_nthresh, status); status = lutzalloc(w, h, &ctx->lutz); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } return status; - exit: +exit: freedeblend(ctx); return status; } @@ -236,8 +256,7 @@ int allocdeblend(int deblend_nthresh, int64_t w, int64_t h, deblendctx *ctx) /* Free the memory allocated by global pointers in refine.c */ -void freedeblend(deblendctx *ctx) -{ +void freedeblend(deblendctx * ctx) { lutzfree(&ctx->lutz); free(ctx->son); ctx->son = NULL; @@ -252,17 +271,15 @@ void freedeblend(deblendctx *ctx) Collect faint remaining pixels and allocate them to their most probable progenitor. */ -int gatherup(objliststruct *objlistin, objliststruct *objlistout) -{ - char *bmp; - float *amp, *p, dx,dy, drand, dist, distmin; - objstruct *objin = objlistin->obj, *objout, *objt; +int gatherup(objliststruct * objlistin, objliststruct * objlistout) { + char * bmp; + float *amp, *p, dx, dy, drand, dist, distmin; + objstruct *objin = objlistin->obj, *objout, *objt; - pliststruct *pixelin = objlistin->plist, *pixelout, *pixt,*pixt2; + pliststruct *pixelin = objlistin->plist, *pixelout, *pixt, *pixt2; - int64_t i,k,l, *n, iclst, npix, bmwidth, - nobj = objlistin->nobj, xs,ys, x,y; - int status; + int64_t i, k, l, *n, iclst, npix, bmwidth, nobj = objlistin->nobj, xs, ys, x, y; + int status; bmp = NULL; amp = p = NULL; @@ -275,96 +292,96 @@ int gatherup(objliststruct *objlistin, objliststruct *objlistout) QMALLOC(p, float, nobj, status); QMALLOC(n, int64_t, nobj, status); - for (i=1; ixmax - (xs=objin->xmin) + 1; - npix = bmwidth * (objin->ymax - (ys=objin->ymin) + 1); - if (!(bmp = (char *)calloc(1, npix*sizeof(char)))) - { - bmp = NULL; - status = MEMORY_ALLOC_ERROR; - goto exit; - } + bmwidth = objin->xmax - (xs = objin->xmin) + 1; + npix = bmwidth * (objin->ymax - (ys = objin->ymin) + 1); + if (!(bmp = (char *)calloc(1, npix * sizeof(char)))) { + bmp = NULL; + status = MEMORY_ALLOC_ERROR; + goto exit; + } - for (objt = objin+(i=1); ithresh = objlistin->thresh; + for (objt = objin + (i = 1); i < nobj; i++, objt++) { + /*-- Now we have passed the deblending section, reset threshold */ + objt->thresh = objlistin->thresh; - /* ------------ flag pixels which are already allocated */ - for (pixt=pixelin+objin[i].firstpix; pixt>=pixelin; - pixt=pixelin+PLIST(pixt,nextpix)) - bmp[(PLIST(pixt,x)-xs) + (PLIST(pixt,y)-ys)*bmwidth] = '\1'; + /* ------------ flag pixels which are already allocated */ + for (pixt = pixelin + objin[i].firstpix; pixt >= pixelin; + pixt = pixelin + PLIST(pixt, nextpix)) + { + bmp[(PLIST(pixt, x) - xs) + (PLIST(pixt, y) - ys) * bmwidth] = '\1'; + } - status = addobjdeep(i, objlistin, objlistout); - if (status != RETURN_OK) - goto exit; - n[i] = objlistout->nobj - 1; + status = addobjdeep(i, objlistin, objlistout); + if (status != RETURN_OK) { + goto exit; + } + n[i] = objlistout->nobj - 1; - dist = objt->fdnpix/(2*PI*objt->abcor*objt->a*objt->b); - amp[i] = dist<70.0? objt->thresh * expf(dist) : 4.0*objt->fdpeak; + dist = objt->fdnpix / (2 * PI * objt->abcor * objt->a * objt->b); + amp[i] = dist < 70.0 ? objt->thresh * expf(dist) : 4.0 * objt->fdpeak; - /* ------------ limitate expansion ! */ - if (amp[i]>4.0*objt->fdpeak) - amp[i] = 4.0*objt->fdpeak; + /* ------------ limitate expansion ! */ + if (amp[i] > 4.0 * objt->fdpeak) { + amp[i] = 4.0 * objt->fdpeak; } + } - objout = objlistout->obj; /* DO NOT MOVE !!! */ + objout = objlistout->obj; /* DO NOT MOVE !!! */ - if (!(pixelout=realloc(objlistout->plist, - (objlistout->npix + npix)*plistsize))) - { - status = MEMORY_ALLOC_ERROR; - goto exit; - } + if (!(pixelout = realloc(objlistout->plist, (objlistout->npix + npix) * plistsize))) { + status = MEMORY_ALLOC_ERROR; + goto exit; + } objlistout->plist = pixelout; k = objlistout->npix; - iclst = 0; /* To avoid gcc -Wall warnings */ - for (pixt=pixelin+objin->firstpix; pixt>=pixelin; - pixt=pixelin+PLIST(pixt,nextpix)) - { - x = PLIST(pixt,x); - y = PLIST(pixt,y); - if (!bmp[(x-xs) + (y-ys)*bmwidth]) - { - pixt2 = pixelout + (l=(k++*plistsize)); - memcpy(pixt2, pixt, (size_t)plistsize); - PLIST(pixt2, nextpix) = -1; - distmin = 1e+31; - for (objt = objin+(i=1); imx; - dy = y - objt->my; - dist=0.5*(objt->cxx*dx*dx+objt->cyy*dy*dy+objt->cxy*dx*dy)/objt->abcor; - p[i] = p[i-1] + (dist<70.0?amp[i]*expf(-dist) : 0.0); - if (dist 1.0e-31) - { - drand = p[nobj-1]*rand_r(&randseed)/(float)RAND_MAX; - for (i=1; ifirstpix; pixt >= pixelin; + pixt = pixelin + PLIST(pixt, nextpix)) + { + x = PLIST(pixt, x); + y = PLIST(pixt, y); + if (!bmp[(x - xs) + (y - ys) * bmwidth]) { + pixt2 = pixelout + (l = (k++ * plistsize)); + memcpy(pixt2, pixt, (size_t)plistsize); + PLIST(pixt2, nextpix) = -1; + distmin = 1e+31; + for (objt = objin + (i = 1); i < nobj; i++, objt++) { + dx = x - objt->mx; + dy = y - objt->my; + dist = 0.5 * (objt->cxx * dx * dx + objt->cyy * dy * dy + objt->cxy * dx * dy) + / objt->abcor; + p[i] = p[i - 1] + (dist < 70.0 ? amp[i] * expf(-dist) : 0.0); + if (dist < distmin) { + distmin = dist; + iclst = i; + } + } + if (p[nobj - 1] > 1.0e-31) { + drand = p[nobj - 1] * rand_r(&randseed) / (float)RAND_MAX; + for (i = 1; i < nobj && p[i] < drand; i++) + ; + if (i == nobj) { + i = iclst; + } + } else { + i = iclst; + } + objout[n[i]].lastpix = PLIST(pixelout + objout[n[i]].lastpix, nextpix) = l; } + } objlistout->npix = k; - if (!(objlistout->plist = realloc(pixelout, - objlistout->npix*plistsize))) + if (!(objlistout->plist = realloc(pixelout, objlistout->npix * plistsize))) { status = MEMORY_ALLOC_ERROR; + } - exit: +exit: free(bmp); free(amp); free(p); @@ -379,18 +396,19 @@ int gatherup(objliststruct *objlistin, objliststruct *objlistout) * first object are included in the pixels of the second object. */ -int belong(int corenb, objliststruct *coreobjlist, - int shellnb, objliststruct *shellobjlist) -{ - objstruct *cobj = &(coreobjlist->obj[corenb]), - *sobj = &(shellobjlist->obj[shellnb]); +int belong( + int corenb, objliststruct * coreobjlist, int shellnb, objliststruct * shellobjlist +) { + objstruct *cobj = &(coreobjlist->obj[corenb]), *sobj = &(shellobjlist->obj[shellnb]); pliststruct *cpl = coreobjlist->plist, *spl = shellobjlist->plist, *pixt; - int64_t xc=PLIST(cpl+cobj->firstpix,x), yc=PLIST(cpl+cobj->firstpix,y); + int64_t xc = PLIST(cpl + cobj->firstpix, x), yc = PLIST(cpl + cobj->firstpix, y); - for (pixt = spl+sobj->firstpix; pixt>=spl; pixt = spl+PLIST(pixt,nextpix)) - if ((PLIST(pixt,x) == xc) && (PLIST(pixt,y) == yc)) + for (pixt = spl + sobj->firstpix; pixt >= spl; pixt = spl + PLIST(pixt, nextpix)) { + if ((PLIST(pixt, x) == xc) && (PLIST(pixt, y) == yc)) { return 1; + } + } return 0; } @@ -400,14 +418,19 @@ int belong(int corenb, objliststruct *coreobjlist, /* Create pixel-index submap for deblending. */ -int64_t *createsubmap(objliststruct *objlistin, int64_t no, - int64_t *subx, int64_t *suby, int64_t *subw, int64_t *subh) -{ - objstruct *obj; - pliststruct *pixel, *pixt; - int64_t i, n, xmin,ymin, w, *pix, *pt, *submap; - - obj = objlistin->obj+no; +int64_t * createsubmap( + objliststruct * objlistin, + int64_t no, + int64_t * subx, + int64_t * suby, + int64_t * subw, + int64_t * subh +) { + objstruct * obj; + pliststruct *pixel, *pixt; + int64_t i, n, xmin, ymin, w, *pix, *pt, *submap; + + obj = objlistin->obj + no; pixel = objlistin->plist; *subx = xmin = obj->xmin; @@ -415,18 +438,19 @@ int64_t *createsubmap(objliststruct *objlistin, int64_t no, *subw = w = obj->xmax - xmin + 1; *subh = obj->ymax - ymin + 1; - n = w**subh; - if (!(submap = pix = malloc(n*sizeof(int64_t)))) + n = w * *subh; + if (!(submap = pix = malloc(n * sizeof(int64_t)))) { return NULL; + } pt = pix; - for (i=n; i--;) + for (i = n; i--;) { *(pt++) = -1; + } - for (i=obj->firstpix; i!=-1; i=PLIST(pixt,nextpix)) - { - pixt = pixel+i; - *(pix+(PLIST(pixt,x)-xmin) + (PLIST(pixt,y)-ymin)*w) = i; - } + for (i = obj->firstpix; i != -1; i = PLIST(pixt, nextpix)) { + pixt = pixel + i; + *(pix + (PLIST(pixt, x) - xmin) + (PLIST(pixt, y) - ymin) * w) = i; + } return submap; } diff --git a/src/extract.c b/src/extract.c index fbbdea2..7bffe95 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,40 +1,41 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* Note: was scan.c in SExtractor. */ #include #include +#include #include #include -#include #include + +#include "extract.h" #include "sep.h" #include "sepcore.h" -#include "extract.h" -#define DETECT_MAXAREA 0 /* replaces prefs.ext_maxarea */ -#define WTHRESH_CONVFAC 1e-4 /* Factor to apply to weights when */ - /* thresholding filtered weight-maps */ +#define DETECT_MAXAREA 0 /* replaces prefs.ext_maxarea */ +#define WTHRESH_CONVFAC 1e-4 /* Factor to apply to weights when */ +/* thresholding filtered weight-maps */ /* globals */ _Thread_local int64_t plistexist_cdvalue, plistexist_thresh, plistexist_var; @@ -44,38 +45,66 @@ _Thread_local unsigned int randseed; static _Atomic size_t extract_pixstack = 300000; /* get and set pixstack */ -void sep_set_extract_pixstack(size_t val) -{ +void sep_set_extract_pixstack(size_t val) { extract_pixstack = val; } -size_t sep_get_extract_pixstack() -{ +size_t sep_get_extract_pixstack() { return extract_pixstack; } -int sortit(infostruct *info, objliststruct *objlist, int minarea, - objliststruct *finalobjlist, int deblend_nthresh, - double deblend_mincont, double gain, deblendctx *deblendctx); -int segsortit(infostruct *info, objliststruct *objlist, - objliststruct *finalobjlist, double gain); +int sortit( + infostruct * info, + objliststruct * objlist, + int minarea, + objliststruct * finalobjlist, + int deblend_nthresh, + double deblend_mincont, + double gain, + deblendctx * deblendctx +); +int segsortit( + infostruct * info, + objliststruct * objlist, + objliststruct * finalobjlist, + double gain +); void plistinit(int hasconv, int hasvar); -void clean(objliststruct *objlist, double clean_param, int *survives); -PIXTYPE get_mean_thresh(infostruct *info, pliststruct *pixel); -int convert_to_catalog(objliststruct *objlist, const int *survives, - sep_catalog *cat, int64_t w, int include_pixels); - -int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, - int64_t w, int64_t h, int64_t bufw, int64_t bufh); -void arraybuffer_readline(arraybuffer *buf); -void arraybuffer_free(arraybuffer *buf); +void clean(objliststruct * objlist, double clean_param, int * survives); +PIXTYPE get_mean_thresh(infostruct * info, pliststruct * pixel); +int convert_to_catalog( + objliststruct * objlist, + const int * survives, + sep_catalog * cat, + int64_t w, + int include_pixels +); + +int arraybuffer_init( + arraybuffer * buf, + const void * arr, + int dtype, + int64_t w, + int64_t h, + int64_t bufw, + int64_t bufh +); +void arraybuffer_readline(arraybuffer * buf); +void arraybuffer_free(arraybuffer * buf); /********************* array buffer functions ********************************/ /* initialize buffer */ /* bufw must be less than or equal to w */ -int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, - int64_t w, int64_t h, int64_t bufw, int64_t bufh) { +int arraybuffer_init( + arraybuffer * buf, + const void * arr, + int dtype, + int64_t w, + int64_t h, + int64_t bufw, + int64_t bufh +) { int status; int64_t yl; status = RETURN_OK; @@ -87,52 +116,55 @@ int arraybuffer_init(arraybuffer *buf, const void *arr, int dtype, /* buffer array info */ buf->bptr = NULL; - QMALLOC(buf->bptr, PIXTYPE, bufw*bufh, status); + QMALLOC(buf->bptr, PIXTYPE, bufw * bufh, status); buf->bw = bufw; buf->bh = bufh; /* pointers to within buffer */ - buf->midline = buf->bptr + bufw*(bufh/2); /* ptr to middle buffer line */ - buf->lastline = buf->bptr + bufw*(bufh-1); /* ptr to last buffer line */ + buf->midline = buf->bptr + bufw * (bufh / 2); /* ptr to middle buffer line */ + buf->lastline = buf->bptr + bufw * (bufh - 1); /* ptr to last buffer line */ status = get_array_converter(dtype, &(buf->readline), &(buf->elsize)); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } /* initialize yoff */ buf->yoff = -bufh; /* read in lines until the first data line is one line above midline */ - for (yl=0; yl < bufh - bufh/2 - 1; yl++) + for (yl = 0; yl < bufh - bufh / 2 - 1; yl++) { arraybuffer_readline(buf); + } return status; - exit: +exit: free(buf->bptr); buf->bptr = NULL; return status; } /* read a line into the buffer at the top, shifting all lines down one */ -void arraybuffer_readline(arraybuffer *buf) { - PIXTYPE *line; +void arraybuffer_readline(arraybuffer * buf) { + PIXTYPE * line; int64_t y; /* shift all lines down one */ - for (line = buf->bptr; line < buf->lastline; line += buf->bw) + for (line = buf->bptr; line < buf->lastline; line += buf->bw) { memcpy(line, line + buf->bw, sizeof(PIXTYPE) * buf->bw); + } /* which image line now corresponds to the last line in buffer? */ buf->yoff++; y = buf->yoff + buf->bh - 1; - if (y < buf->dh) - buf->readline(buf->dptr + buf->elsize * buf->dw * y, buf->dw, - buf->lastline); + if (y < buf->dh) { + buf->readline(buf->dptr + buf->elsize * buf->dw * y, buf->dw, buf->lastline); + } } -void arraybuffer_free(arraybuffer *buf) { +void arraybuffer_free(arraybuffer * buf) { free(buf->bptr); buf->bptr = NULL; } @@ -155,10 +187,10 @@ void arraybuffer_free(arraybuffer *buf) { * So, this routine sets masked pixels to zero in the image buffer and * infinity in the noise buffer (if present). It affects the first */ -void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf) { +void apply_mask_line(arraybuffer * mbuf, arraybuffer * imbuf, arraybuffer * nbuf) { int64_t i; - for (i=0; ibw; i++) { + for (i = 0; i < mbuf->bw; i++) { if (mbuf->lastline[i] > 0.0) { imbuf->lastline[i] = 0.0; if (nbuf) { @@ -169,41 +201,51 @@ void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf) { } /****************************** extract **************************************/ -int sep_extract(const sep_image *image, float thresh, int thresh_type, - int minarea, const float *conv, int64_t convw, int64_t convh, - int filter_type, int deblend_nthresh, double deblend_cont, - int clean_flag, double clean_param, sep_catalog **catalog) { - - arraybuffer dbuf, nbuf, mbuf, sbuf; - infostruct curpixinfo, initinfo, freeinfo; - objliststruct objlist; - char newmarker; - size_t mem_pixstack; - int64_t nposize, oldnposize; - int64_t w, h; - int64_t co, i, pstop, xl, xl2, yl, cn; - int64_t ididx, numids, totnpix; - int64_t prevpix, bufh; - int64_t stacksize, convn; - int status, isvarthresh, isvarnoise, luflag; - short trunflag; - PIXTYPE relthresh, cdnewsymbol, pixvar, pixsig; - float sum; - pixstatus cs, ps; - - infostruct *info, *store, *idinfo; - objliststruct *finalobjlist; - pliststruct *pixel, *pixt; - char *marker; - PIXTYPE *scan, *cdscan, *wscan, *dummyscan, *sscan; - PIXTYPE *sigscan, *workscan; - float *convnorm; - int64_t *start, *end, *cumcounts; - int *survives; - pixstatus *psstack; - char errtext[512]; - sep_catalog *cat; - deblendctx deblendctx; +int sep_extract( + const sep_image * image, + float thresh, + int thresh_type, + int minarea, + const float * conv, + int64_t convw, + int64_t convh, + int filter_type, + int deblend_nthresh, + double deblend_cont, + int clean_flag, + double clean_param, + sep_catalog ** catalog +) { + arraybuffer dbuf, nbuf, mbuf, sbuf; + infostruct curpixinfo, initinfo, freeinfo; + objliststruct objlist; + char newmarker; + size_t mem_pixstack; + int64_t nposize, oldnposize; + int64_t w, h; + int64_t co, i, pstop, xl, xl2, yl, cn; + int64_t ididx, numids, totnpix; + int64_t prevpix, bufh; + int64_t stacksize, convn; + int status, isvarthresh, isvarnoise, luflag; + short trunflag; + PIXTYPE relthresh, cdnewsymbol, pixvar, pixsig; + float sum; + pixstatus cs, ps; + + infostruct *info, *store, *idinfo; + objliststruct * finalobjlist; + pliststruct *pixel, *pixt; + char * marker; + PIXTYPE *scan, *cdscan, *wscan, *dummyscan, *sscan; + PIXTYPE *sigscan, *workscan; + float * convnorm; + int64_t *start, *end, *cumcounts; + int * survives; + pixstatus * psstack; + char errtext[512]; + sep_catalog * cat; + deblendctx deblendctx; status = RETURN_OK; pixel = NULL; @@ -240,11 +282,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, QCALLOC(cumcounts, int64_t, numids, status); QCALLOC(idinfo, infostruct, numids, status); totnpix = 0; - for (i=0; iidcounts[i]; } - if ((size_t)totnpix>mem_pixstack) { + if ((size_t)totnpix > mem_pixstack) { goto exit; } } @@ -254,40 +296,39 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, randseed = 1; /* Noise characteristics of the image: None, scalar or variable? */ - if (image->noise_type == SEP_NOISE_NONE) {} /* nothing to do */ - else if (image->noise == NULL) { + if (image->noise_type == SEP_NOISE_NONE) { + } /* nothing to do */ + else if (image->noise == NULL) + { /* noise is constant; we can set pixel noise now. */ if (image->noise_type == SEP_NOISE_STDDEV) { pixsig = image->noiseval; pixvar = pixsig * pixsig; - } - else if (image->noise_type == SEP_NOISE_VAR) { + } else if (image->noise_type == SEP_NOISE_VAR) { pixvar = image->noiseval; pixsig = sqrt(pixvar); - } - else { + } else { return UNKNOWN_NOISE_TYPE; } - } - else { + } else { /* noise is variable; we deal with setting pixvar and pixsig at each * pixel. */ isvarnoise = 1; } /* Deal with relative thresholding. (For an absolute threshold - * nothing needs to be done, as `thresh` should already contain the constant - * threshold, and `isvarthresh` is already 0.) */ + * nothing needs to be done, as `thresh` should already contain the constant + * threshold, and `isvarthresh` is already 0.) */ if (thresh_type == SEP_THRESH_REL) { - /* The image must have noise information. */ - if (image->noise_type == SEP_NOISE_NONE) return RELTHRESH_NO_NOISE; + if (image->noise_type == SEP_NOISE_NONE) { + return RELTHRESH_NO_NOISE; + } - isvarthresh = isvarnoise; /* threshold is variable if noise is */ + isvarthresh = isvarnoise; /* threshold is variable if noise is */ if (isvarthresh) { relthresh = thresh; /* used below to set `thresh` for each pixel. */ - } - else { + } else { /* thresh is constant; convert relative threshold to absolute */ thresh *= pixsig; } @@ -297,7 +338,7 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, objlist.thresh = thresh; /*Allocate memory for buffers */ - stacksize = w+1; + stacksize = w + 1; QMALLOC(info, infostruct, stacksize, status); QCALLOC(store, infostruct, stacksize, status); QMALLOC(marker, char, stacksize, status); @@ -305,8 +346,9 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, QMALLOC(psstack, pixstatus, stacksize, status); QCALLOC(start, int64_t, stacksize, status); QMALLOC(end, int64_t, stacksize, status); - if ((status = allocdeblend(deblend_nthresh, w, h, &deblendctx)) != RETURN_OK) + if ((status = allocdeblend(deblend_nthresh, w, h, &deblendctx)) != RETURN_OK) { goto exit; + } /* Initialize buffers for input array(s). * The buffer size depends on whether or not convolution is active. @@ -314,32 +356,38 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, * the buffer height equals the height of the convolution kernel. */ bufh = conv ? convh : 1; - status = arraybuffer_init(&dbuf, image->data, image->dtype, w, h, stacksize, - bufh); - if (status != RETURN_OK) goto exit; + status = arraybuffer_init(&dbuf, image->data, image->dtype, w, h, stacksize, bufh); + if (status != RETURN_OK) { + goto exit; + } if (isvarnoise) { - status = arraybuffer_init(&nbuf, image->noise, image->ndtype, w, h, - stacksize, bufh); - if (status != RETURN_OK) goto exit; + status = + arraybuffer_init(&nbuf, image->noise, image->ndtype, w, h, stacksize, bufh); + if (status != RETURN_OK) { + goto exit; + } } if (image->mask) { - status = arraybuffer_init(&mbuf, image->mask, image->mdtype, w, h, - stacksize, bufh); - if (status != RETURN_OK) goto exit; + status = arraybuffer_init(&mbuf, image->mask, image->mdtype, w, h, stacksize, bufh); + if (status != RETURN_OK) { + goto exit; + } } if (image->segmap) { - status = arraybuffer_init(&sbuf, image->segmap, image->sdtype, w, h, - stacksize, bufh); - if (status != RETURN_OK) + status = + arraybuffer_init(&sbuf, image->segmap, image->sdtype, w, h, stacksize, bufh); + if (status != RETURN_OK) { goto exit; + } } /* `scan` (or `wscan`) is always a pointer to the current line being * processed. It might be the only line in the buffer, or it might be the * middle line. */ scan = dbuf.midline; - if (isvarnoise) + if (isvarnoise) { wscan = nbuf.midline; + } /* More initializations */ initinfo.pixnb = 0; @@ -348,15 +396,15 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (image->segmap) { sscan = sbuf.midline; - for (i=0; inoise_type != SEP_NOISE_NONE)); - if (!(pixel = objlist.plist = malloc(nposize=mem_pixstack*plistsize))) { + if (!(pixel = objlist.plist = malloc(nposize = mem_pixstack * plistsize))) { status = MEMORY_ALLOC_ERROR; goto exit; } /*----- at the beginning, "free" object fills the whole pixel list */ freeinfo.firstpix = 0; - freeinfo.lastpix = nposize-plistsize; + freeinfo.lastpix = nposize - plistsize; pixt = pixel; - for (i=plistsize; imask) { arraybuffer_readline(&mbuf); - apply_mask_line(&mbuf, &dbuf, (isvarnoise? &nbuf: NULL)); + apply_mask_line(&mbuf, &dbuf, (isvarnoise ? &nbuf : NULL)); } if (image->segmap) { arraybuffer_readline(&sbuf); @@ -447,72 +497,79 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, /* filter the lines */ if (conv) { status = convolve(&dbuf, yl, convnorm, convw, convh, cdscan); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } - if (filter_type == SEP_FILTER_MATCHED) { - status = matched_filter(&dbuf, &nbuf, yl, convnorm, convw, - convh, workscan, sigscan, - image->noise_type); - - if (status != RETURN_OK) + if (filter_type == SEP_FILTER_MATCHED) { + status = matched_filter( + &dbuf, + &nbuf, + yl, + convnorm, + convw, + convh, + workscan, + sigscan, + image->noise_type + ); + + if (status != RETURN_OK) { goto exit; + } } } else { - cdscan = scan; - } - } - - trunflag = (yl==0 || yl==h-1)? SEP_OBJ_TRUNC: 0; + cdscan = scan; + } + } - for (xl=0; xl<=w; xl++) { + trunflag = (yl == 0 || yl == h - 1) ? SEP_OBJ_TRUNC : 0; - if (xl == w) + for (xl = 0; xl <= w; xl++) { + if (xl == w) { cdnewsymbol = -BIG; - else + } else { cdnewsymbol = cdscan[xl]; + } - newmarker = marker[xl]; /* marker at this pixel */ - marker[xl] = 0; + newmarker = marker[xl]; /* marker at this pixel */ + marker[xl] = 0; - curpixinfo.flag = trunflag; + curpixinfo.flag = trunflag; /* set pixel variance/noise based on noise array */ if (isvarthresh) { if (xl == w || yl == h) { pixsig = pixvar = 0.0; - } - else if (image->noise_type == SEP_NOISE_VAR) { + } else if (image->noise_type == SEP_NOISE_VAR) { pixvar = wscan[xl]; pixsig = sqrt(pixvar); - } - else if (image->noise_type == SEP_NOISE_STDDEV) { + } else if (image->noise_type == SEP_NOISE_STDDEV) { pixsig = wscan[xl]; pixvar = pixsig * pixsig; - } - else { + } else { status = UNKNOWN_NOISE_TYPE; goto exit; } /* set `thresh` (This is needed later, even - * if filter_type is SEP_FILTER_MATCHED */ + * if filter_type is SEP_FILTER_MATCHED */ thresh = relthresh * pixsig; } /* luflag: is pixel above thresh (Y/N)? */ /* First check if segmap exists */ if (image->segmap) { - if ((sscan[xl]>0) && (xl != w) && (yl != h)) { - if (xl == 0 || xl == w - 1) + if ((sscan[xl] > 0) && (xl != w) && (yl != h)) { + if (xl == 0 || xl == w - 1) { curpixinfo.flag |= SEP_OBJ_TRUNC; + } - for (ididx=0; ididxsegids[ididx]==(int64_t)sscan[xl]) { - - pixt = pixel + prevpix*plistsize; + for (ididx = 0; ididx < numids; ididx++) { + if (image->segids[ididx] == (int64_t)sscan[xl]) { + pixt = pixel + prevpix * plistsize; prevpix = cumcounts[ididx] + idinfo[ididx].pixnb; - pixt = pixel + prevpix*plistsize; + pixt = pixel + prevpix * plistsize; PLIST(pixt, x) = xl; PLIST(pixt, y) = yl; @@ -528,12 +585,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, }; if (idinfo[ididx].pixnb == 0) { - idinfo[ididx].firstpix = prevpix*plistsize; + idinfo[ididx].firstpix = prevpix * plistsize; idinfo[ididx].pixnb = 1; - } - else if (idinfo[ididx].pixnb == image->idcounts[ididx]-1) { + } else if (idinfo[ididx].pixnb == image->idcounts[ididx] - 1) { idinfo[ididx].pixnb++; - idinfo[ididx].lastpix = prevpix*plistsize; + idinfo[ididx].lastpix = prevpix * plistsize; PLIST(pixt, nextpix) = -1; } else { idinfo[ididx].pixnb++; @@ -543,21 +599,21 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, } } } else { + if (filter_type == SEP_FILTER_MATCHED) { + luflag = ((xl != w) && (sigscan[xl] > relthresh)) ? 1 : 0; + } else { + luflag = cdnewsymbol > thresh ? 1 : 0; + } - if (filter_type == SEP_FILTER_MATCHED) - luflag = ((xl != w) && (sigscan[xl] > relthresh))? 1: 0; - else - luflag = cdnewsymbol > thresh? 1: 0; - - if (luflag) { + if (luflag) { /* flag the current object if we're near the image bounds */ - if (xl==0 || xl==w-1) { - curpixinfo.flag |= SEP_OBJ_TRUNC; + if (xl == 0 || xl == w - 1) { + curpixinfo.flag |= SEP_OBJ_TRUNC; }; /* point pixt to first free pixel in pixel list */ /* and increment the "first free pixel" */ - pixt = pixel + (cn=freeinfo.firstpix); + pixt = pixel + (cn = freeinfo.firstpix); freeinfo.firstpix = PLIST(pixt, nextpix); curpixinfo.lastpix = curpixinfo.firstpix = cn; @@ -576,26 +632,28 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, PLISTPIX(pixt, thresh) = thresh; }; - /* Check if we have run out of free pixels in objlist.plist */ - if (freeinfo.firstpix==freeinfo.lastpix) { + /* Check if we have run out of free pixels in objlist.plist */ + if (freeinfo.firstpix == freeinfo.lastpix) { status = PIXSTACK_FULL; - sprintf(errtext, - "The limit of %d active object pixels over the " - "detection threshold was reached. Check that " - "the image is background subtracted and the " - "detection threshold is not too low. If you " - "need to increase the limit, use " - "set_extract_pixstack.", - (int)mem_pixstack); + sprintf( + errtext, + "The limit of %d active object pixels over the " + "detection threshold was reached. Check that " + "the image is background subtracted and the " + "detection threshold is not too low. If you " + "need to increase the limit, use " + "set_extract_pixstack.", + (int)mem_pixstack + ); put_errdetail(errtext); goto exit; /* The code in the rest of this block increases the - * stack size as needed. Currently, it is never - * executed. This is because it isn't clear that - * this is a good idea: most times when the stack - * overflows it is due to user error: too-low - * threshold or image not background subtracted. */ + * stack size as needed. Currently, it is never + * executed. This is because it isn't clear that + * this is a good idea: most times when the stack + * overflows it is due to user error: too-low + * threshold or image not background subtracted. */ /* increase the stack size */ oldnposize = nposize; @@ -609,12 +667,14 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, } /* set next free pixel to the start of the new block - * and link up all the pixels in the new block */ - PLIST(pixel+freeinfo.firstpix, nextpix) = oldnposize; + * and link up all the pixels in the new block */ + PLIST(pixel + freeinfo.firstpix, nextpix) = oldnposize; pixt = pixel + oldnposize; - for (i=oldnposize + plistsize; i= minarea) { - /* update threshold before object is processed */ - if (PLISTEXIST(thresh)) { + if ((int64_t)info[co].pixnb >= minarea) { + /* update threshold before object is processed */ + if (PLISTEXIST(thresh)) { objlist.thresh = get_mean_thresh(&info[co], objlist.plist); } else { objlist.thresh = thresh; } - status = sortit(&info[co], &objlist, minarea, - finalobjlist, deblend_nthresh, - deblend_cont, - image->gain, &deblendctx); + status = sortit( + &info[co], + &objlist, + minarea, + finalobjlist, + deblend_nthresh, + deblend_cont, + image->gain, + &deblendctx + ); if (status != RETURN_OK) { goto exit; }; - } + } /* free the chain-list */ - PLIST(pixel+info[co].lastpix, nextpix) = freeinfo.firstpix; + PLIST(pixel + info[co].lastpix, nextpix) = freeinfo.firstpix; freeinfo.firstpix = info[co].firstpix; - } else { + } else { marker[end[co]] = 'F'; store[start[co]] = info[co]; } co--; ps = psstack[--pstop]; - } - } - } - /* end of if (newmarker) ------------------------------------------*/ + } + } + } + /* end of if (newmarker) ------------------------------------------*/ /* update the info or end segment */ if (luflag) { @@ -724,9 +789,9 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, store[start[co]] = info[co]; co--; } - } + } } - } /*------------ End of the loop over the x's -----------------------*/ + } /*------------ End of the loop over the x's -----------------------*/ } /*---------------- End of the loop over the y's -----------------------*/ if (image->segmap) { @@ -738,10 +803,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, /* if cleaning, see which objects "survive" cleaning. */ if (clean_flag) { /* Calculate mthresh for all objects in the list (needed for cleaning) */ - for (i=0; inobj; i++) { - status = analysemthresh(i, finalobjlist, minarea, thresh); - if (status != RETURN_OK) + for (i = 0; i < finalobjlist->nobj; i++) { + status = analysemthresh(i, finalobjlist, minarea, thresh); + if (status != RETURN_OK) { goto exit; + } } QMALLOC(survives, int, finalobjlist->nobj, status); @@ -751,9 +817,11 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, /* convert to output catalog */ QCALLOC(cat, sep_catalog, 1, status); status = convert_to_catalog(finalobjlist, survives, cat, w, 1); - if (status != RETURN_OK) goto exit; + if (status != RETURN_OK) { + goto exit; + } - exit: +exit: if (finalobjlist) { free(finalobjlist->obj); free(finalobjlist->plist); @@ -775,12 +843,15 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, free(end); free(survives); arraybuffer_free(&dbuf); - if (image->noise) + if (image->noise) { arraybuffer_free(&nbuf); - if (image->mask) + } + if (image->mask) { arraybuffer_free(&mbuf); - if (conv) + } + if (conv) { free(convnorm); + } if (filter_type == SEP_FILTER_MATCHED) { free(sigscan); free(workscan); @@ -788,8 +859,9 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, if (status != RETURN_OK) { /* free cdscan if we didn't do it on the last `yl` line */ - if (conv && (cdscan != dummyscan)) + if (conv && (cdscan != dummyscan)) { free(cdscan); + } /* clean up catalog if it was allocated */ sep_catalog_free(cat); @@ -800,8 +872,12 @@ int sep_extract(const sep_image *image, float thresh, int thresh_type, return status; } -int segsortit(infostruct *info, objliststruct *objlist, - objliststruct *finalobjlist, double gain) { +int segsortit( + infostruct * info, + objliststruct * objlist, + objliststruct * finalobjlist, + double gain +) { objstruct obj; int status; @@ -817,14 +893,16 @@ int segsortit(infostruct *info, objliststruct *objlist, obj.lastpix = info->lastpix; obj.flag = info->flag; - obj.thresh = plistexist_thresh ? get_mean_thresh(info, objlist->plist): objlist->thresh; + obj.thresh = + plistexist_thresh ? get_mean_thresh(info, objlist->plist) : objlist->thresh; analyse(0, objlist, 1, gain); status = addobjdeep(0, objlist, finalobjlist); - if (status != RETURN_OK) + if (status != RETURN_OK) { goto exit; + } exit: return status; @@ -834,17 +912,22 @@ int segsortit(infostruct *info, objliststruct *objlist, /* build the object structure. */ -int sortit(infostruct *info, objliststruct *objlist, int minarea, - objliststruct *finalobjlist, - int deblend_nthresh, double deblend_mincont, double gain, - deblendctx *deblendctx) -{ - objliststruct objlistout, *objlist2; - objstruct obj; - int64_t i; +int sortit( + infostruct * info, + objliststruct * objlist, + int minarea, + objliststruct * finalobjlist, + int deblend_nthresh, + double deblend_mincont, + double gain, + deblendctx * deblendctx +) { + objliststruct objlistout, *objlist2; + objstruct obj; + int64_t i; int status; - status=RETURN_OK; + status = RETURN_OK; objlistout.obj = NULL; objlistout.plist = NULL; objlistout.nobj = objlistout.npix = 0; @@ -862,38 +945,40 @@ int sortit(infostruct *info, objliststruct *objlist, int minarea, preanalyse(0, objlist); - status = deblend(objlist, &objlistout, deblend_nthresh, deblend_mincont, - minarea, deblendctx); - if (status) - { - /* formerly, this wasn't a fatal error, so a flag was set for - * the object and we continued. I'm leaving the flag-setting - * here in case we want to change this to a non-fatal error in - * the future, but currently the flag setting is irrelevant. */ - objlist2 = objlist; - for (i=0; inobj; i++) - objlist2->obj[i].flag |= SEP_OBJ_DOVERFLOW; - goto exit; + status = deblend( + objlist, &objlistout, deblend_nthresh, deblend_mincont, minarea, deblendctx + ); + if (status) { + /* formerly, this wasn't a fatal error, so a flag was set for + * the object and we continued. I'm leaving the flag-setting + * here in case we want to change this to a non-fatal error in + * the future, but currently the flag setting is irrelevant. */ + objlist2 = objlist; + for (i = 0; i < objlist2->nobj; i++) { + objlist2->obj[i].flag |= SEP_OBJ_DOVERFLOW; } - else + goto exit; + } else { objlist2 = &objlistout; + } /* Analyze the deblended objects and add to the final list */ - for (i=0; inobj; i++) - { - analyse(i, objlist2, 1, gain); - - /* this does nothing if DETECT_MAXAREA is 0 (and it currently is) */ - if (DETECT_MAXAREA && objlist2->obj[i].fdnpix > DETECT_MAXAREA) - continue; - - /* add the object to the final list */ - status = addobjdeep(i, objlist2, finalobjlist); - if (status != RETURN_OK) - goto exit; + for (i = 0; i < objlist2->nobj; i++) { + analyse(i, objlist2, 1, gain); + + /* this does nothing if DETECT_MAXAREA is 0 (and it currently is) */ + if (DETECT_MAXAREA && objlist2->obj[i].fdnpix > DETECT_MAXAREA) { + continue; } - exit: + /* add the object to the final list */ + status = addobjdeep(i, objlist2, finalobjlist); + if (status != RETURN_OK) { + goto exit; + } + } + +exit: free(objlistout.plist); free(objlistout.obj); return status; @@ -906,104 +991,95 @@ Add object number `objnb` from list `objl1` to list `objl2`. Unlike `addobjshallow` this also copies plist pixels to the second list. */ -int addobjdeep(int objnb, objliststruct *objl1, objliststruct *objl2) -{ - objstruct *objl2obj; - pliststruct *plist1 = objl1->plist, *plist2 = objl2->plist; - int64_t fp, i, j, npx, objnb2; +int addobjdeep(int objnb, objliststruct * objl1, objliststruct * objl2) { + objstruct * objl2obj; + pliststruct *plist1 = objl1->plist, *plist2 = objl2->plist; + int64_t fp, i, j, npx, objnb2; - fp = objl2->npix; /* 2nd list's plist size in pixels */ - j = fp*plistsize; /* 2nd list's plist size in bytes */ - objnb2 = objl2->nobj; /* # of objects currently in 2nd list*/ + fp = objl2->npix; /* 2nd list's plist size in pixels */ + j = fp * plistsize; /* 2nd list's plist size in bytes */ + objnb2 = objl2->nobj; /* # of objects currently in 2nd list*/ /* Allocate space in `objl2` for the new object */ - if (objnb2) - objl2obj = realloc(objl2->obj, - (++objl2->nobj)*sizeof(objstruct)); - else - objl2obj = malloc((++objl2->nobj)*sizeof(objstruct)); + if (objnb2) { + objl2obj = realloc(objl2->obj, (++objl2->nobj) * sizeof(objstruct)); + } else { + objl2obj = malloc((++objl2->nobj) * sizeof(objstruct)); + } - if (!objl2obj) + if (!objl2obj) { goto earlyexit; + } objl2->obj = objl2obj; /* Allocate space for the new object's pixels in 2nd list's plist */ npx = objl1->obj[objnb].fdnpix; - if (fp) - plist2 = realloc(plist2, (objl2->npix+=npx)*plistsize); - else - plist2 = malloc((objl2->npix=npx)*plistsize); + if (fp) { + plist2 = realloc(plist2, (objl2->npix += npx) * plistsize); + } else { + plist2 = malloc((objl2->npix = npx) * plistsize); + } - if (!plist2) + if (!plist2) { goto earlyexit; + } objl2->plist = plist2; /* copy the plist */ plist2 += j; - for(i=objl1->obj[objnb].firstpix; i!=-1; i=PLIST(plist1+i,nextpix)) - { - memcpy(plist2, plist1+i, (size_t)plistsize); - PLIST(plist2,nextpix) = (j+=plistsize); - plist2 += plistsize; - } - PLIST(plist2-=plistsize, nextpix) = -1; + for (i = objl1->obj[objnb].firstpix; i != -1; i = PLIST(plist1 + i, nextpix)) { + memcpy(plist2, plist1 + i, (size_t)plistsize); + PLIST(plist2, nextpix) = (j += plistsize); + plist2 += plistsize; + } + PLIST(plist2 -= plistsize, nextpix) = -1; /* copy the object itself */ objl2->obj[objnb2] = objl1->obj[objnb]; - objl2->obj[objnb2].firstpix = fp*plistsize; - objl2->obj[objnb2].lastpix = j-plistsize; + objl2->obj[objnb2].firstpix = fp * plistsize; + objl2->obj[objnb2].lastpix = j - plistsize; return RETURN_OK; /* if early exit, reset 2nd list */ - earlyexit: +earlyexit: objl2->nobj--; objl2->npix = fp; return MEMORY_ALLOC_ERROR; } - /****************************** plistinit ************************************ * (originally init_plist() in sextractor) PURPOSE initialize a pixel-list and its components. ***/ -void plistinit(int hasconv, int hasvar) -{ - pbliststruct *pbdum = NULL; +void plistinit(int hasconv, int hasvar) { + pbliststruct * pbdum = NULL; plistsize = sizeof(pbliststruct); plistoff_value = (char *)&pbdum->value - (char *)pbdum; - if (hasconv) - { - plistexist_cdvalue = 1; - plistoff_cdvalue = plistsize; - plistsize += sizeof(PIXTYPE); - } - else - { - plistexist_cdvalue = 0; - plistoff_cdvalue = plistoff_value; - } - - if (hasvar) - { - plistexist_var = 1; - plistoff_var = plistsize; - plistsize += sizeof(PIXTYPE); - - plistexist_thresh = 1; - plistoff_thresh = plistsize; - plistsize += sizeof(PIXTYPE); - } - else - { - plistexist_var = 0; - plistexist_thresh = 0; - } + if (hasconv) { + plistexist_cdvalue = 1; + plistoff_cdvalue = plistsize; + plistsize += sizeof(PIXTYPE); + } else { + plistexist_cdvalue = 0; + plistoff_cdvalue = plistoff_value; + } + if (hasvar) { + plistexist_var = 1; + plistoff_var = plistsize; + plistsize += sizeof(PIXTYPE); + plistexist_thresh = 1; + plistoff_thresh = plistsize; + plistsize += sizeof(PIXTYPE); + } else { + plistexist_var = 0; + plistexist_thresh = 0; + } } @@ -1013,96 +1089,101 @@ Fill a list with whether each object in the list survived the cleaning (assumes that mthresh has already been calculated for all objects in the list) */ -void clean(objliststruct *objlist, double clean_param, int *survives) -{ - objstruct *obj1, *obj2; - int64_t i,j; - double amp,ampin,alpha,alphain, unitarea,unitareain,beta,val; - float dx,dy,rlim; +void clean(objliststruct * objlist, double clean_param, int * survives) { + objstruct *obj1, *obj2; + int64_t i, j; + double amp, ampin, alpha, alphain, unitarea, unitareain, beta, val; + float dx, dy, rlim; beta = clean_param; /* initialize to all surviving */ - for (i=0; inobj; i++) + for (i = 0; i < objlist->nobj; i++) { survives[i] = 1; + } obj1 = objlist->obj; - for (i=0; inobj; i++, obj1++) - { - if (!survives[i]) - continue; - - /* parameters for test object */ - unitareain = PI*obj1->a*obj1->b; - ampin = obj1->fdflux/(2*unitareain*obj1->abcor); - alphain = (pow(ampin/obj1->thresh, 1.0/beta)-1)*unitareain/obj1->fdnpix; - - /* loop over remaining objects in list*/ - obj2 = obj1 + 1; - for (j=i+1; jnobj; j++, obj2++) - { - if (!survives[j]) - continue; - - dx = obj1->mx - obj2->mx; - dy = obj1->my - obj2->my; - rlim = obj1->a + obj2->a; - rlim *= rlim; - if (dx*dx + dy*dy > rlim*CLEAN_ZONE*CLEAN_ZONE) - continue; - - /* if obj1 is bigger, see if it eats obj2 */ - if (obj2->fdflux < obj1->fdflux) - { - val = 1 + alphain*(obj1->cxx*dx*dx + obj1->cyy*dy*dy + - obj1->cxy*dx*dy); - if (val>1.0 && ((float)(val<1e10?ampin*pow(val,-beta):0.0) > - obj2->mthresh)) - survives[j] = 0; /* the test object eats this one */ - } - - /* if obj2 is bigger, see if it eats obj1 */ - else - { - unitarea = PI*obj2->a*obj2->b; - amp = obj2->fdflux/(2*unitarea*obj2->abcor); - alpha = (pow(amp/obj2->thresh, 1.0/beta) - 1) * - unitarea/obj2->fdnpix; - val = 1 + alpha*(obj2->cxx*dx*dx + obj2->cyy*dy*dy + - obj2->cxy*dx*dy); - if (val>1.0 && ((float)(val<1e10?amp*pow(val,-beta):0.0) > - obj1->mthresh)) - survives[i] = 0; /* this object eats the test object */ - } - - } /* inner loop over objlist (obj2) */ - } /* outer loop of objlist (obj1) */ + for (i = 0; i < objlist->nobj; i++, obj1++) { + if (!survives[i]) { + continue; + } + + /* parameters for test object */ + unitareain = PI * obj1->a * obj1->b; + ampin = obj1->fdflux / (2 * unitareain * obj1->abcor); + alphain = (pow(ampin / obj1->thresh, 1.0 / beta) - 1) * unitareain / obj1->fdnpix; + + /* loop over remaining objects in list*/ + obj2 = obj1 + 1; + for (j = i + 1; j < objlist->nobj; j++, obj2++) { + if (!survives[j]) { + continue; + } + + dx = obj1->mx - obj2->mx; + dy = obj1->my - obj2->my; + rlim = obj1->a + obj2->a; + rlim *= rlim; + if (dx * dx + dy * dy > rlim * CLEAN_ZONE * CLEAN_ZONE) { + continue; + } + + /* if obj1 is bigger, see if it eats obj2 */ + if (obj2->fdflux < obj1->fdflux) { + val = 1 + + alphain + * (obj1->cxx * dx * dx + obj1->cyy * dy * dy + obj1->cxy * dx * dy); + if (val > 1.0 + && ((float)(val < 1e10 ? ampin * pow(val, -beta) : 0.0) > obj2->mthresh)) + { + survives[j] = 0; /* the test object eats this one */ + } + } + + /* if obj2 is bigger, see if it eats obj1 */ + else + { + unitarea = PI * obj2->a * obj2->b; + amp = obj2->fdflux / (2 * unitarea * obj2->abcor); + alpha = (pow(amp / obj2->thresh, 1.0 / beta) - 1) * unitarea / obj2->fdnpix; + val = + 1 + + alpha * (obj2->cxx * dx * dx + obj2->cyy * dy * dy + obj2->cxy * dx * dy); + if (val > 1.0 + && ((float)(val < 1e10 ? amp * pow(val, -beta) : 0.0) > obj1->mthresh)) + { + survives[i] = 0; /* this object eats the test object */ + } + } + + } /* inner loop over objlist (obj2) */ + } /* outer loop of objlist (obj1) */ } /************************** get_mean_thresh **********************************/ /* Compute an average threshold from all pixels in the cluster */ -PIXTYPE get_mean_thresh(infostruct *info, pliststruct *pixel) -{ - pliststruct *pixt; - int pix_accum=0; - double thresh_accum=0; +PIXTYPE get_mean_thresh(infostruct * info, pliststruct * pixel) { + pliststruct * pixt; + int pix_accum = 0; + double thresh_accum = 0; // Threshold must be cast to double to avoid precision loss - for (pixt=pixel+info->firstpix; pixt>=pixel; pixt=pixel+PLIST(pixt,nextpix)) { - thresh_accum += (double) PLISTPIX(pixt,thresh); + for (pixt = pixel + info->firstpix; pixt >= pixel; + pixt = pixel + PLIST(pixt, nextpix)) + { + thresh_accum += (double)PLISTPIX(pixt, thresh); pix_accum++; } - return (PIXTYPE) (thresh_accum / pix_accum); + return (PIXTYPE)(thresh_accum / pix_accum); } /*****************************************************************************/ /* sep_catalog manipulations */ -void free_catalog_fields(sep_catalog *catalog) -{ +void free_catalog_fields(sep_catalog * catalog) { free(catalog->thresh); free(catalog->npix); free(catalog->tnpix); @@ -1150,14 +1231,18 @@ void free_catalog_fields(sep_catalog *catalog) * `cat`: catalog object to be filled. * `w`: width of image (used to calculate linear indicies). */ -int convert_to_catalog(objliststruct *objlist, const int *survives, - sep_catalog *cat, int64_t w, int include_pixels) -{ +int convert_to_catalog( + objliststruct * objlist, + const int * survives, + sep_catalog * cat, + int64_t w, + int include_pixels +) { int64_t i, j, k; int64_t totnpix; int nobj = 0; int status = RETURN_OK; - objstruct *obj; + objstruct * obj; pliststruct *pixt, *pixel; /* Set struct to zero in case the caller didn't. @@ -1167,10 +1252,13 @@ int convert_to_catalog(objliststruct *objlist, const int *survives, /* Count number of surviving objects so that we can allocate the appropriate amount of space in the output catalog. */ - if (survives) - for (i=0; inobj; i++) nobj += survives[i]; - else + if (survives) { + for (i = 0; i < objlist->nobj; i++) { + nobj += survives[i]; + } + } else { nobj = objlist->nobj; + } /* allocate catalog fields */ cat->nobj = nobj; @@ -1208,99 +1296,97 @@ int convert_to_catalog(objliststruct *objlist, const int *survives, QMALLOC(cat->flag, short, nobj, status); /* fill output arrays */ - j = 0; /* running index in output array */ - for (i=0; inobj; i++) - { - if ((survives == NULL) || survives[i]) - { - obj = objlist->obj + i; - cat->thresh[j] = obj->thresh; - cat->npix[j] = obj->fdnpix; - cat->tnpix[j] = obj->dnpix; - cat->xmin[j] = obj->xmin; - cat->xmax[j] = obj->xmax; - cat->ymin[j] = obj->ymin; - cat->ymax[j] = obj->ymax; - cat->x[j] = obj->mx; - cat->y[j] = obj->my; - cat->x2[j] = obj->mx2; - cat->y2[j] = obj->my2; - cat->xy[j] = obj->mxy; - cat->errx2[j] = obj->errx2; - cat->erry2[j] = obj->erry2; - cat->errxy[j] = obj->errxy; - - cat->a[j] = obj->a; - cat->b[j] = obj->b; - cat->theta[j] = obj->theta; - - cat->cxx[j] = obj->cxx; - cat->cyy[j] = obj->cyy; - cat->cxy[j] = obj->cxy; - - cat->cflux[j] = obj->fdflux; /* these change names */ - cat->flux[j] = obj->dflux; - cat->cpeak[j] = obj->fdpeak; - cat->peak[j] = obj->dpeak; - - cat->xpeak[j] = obj->xpeak; - cat->ypeak[j] = obj->ypeak; - cat->xcpeak[j] = obj->xcpeak; - cat->ycpeak[j] = obj->ycpeak; - - cat->flag[j] = obj->flag; - - j++; - } + j = 0; /* running index in output array */ + for (i = 0; i < objlist->nobj; i++) { + if ((survives == NULL) || survives[i]) { + obj = objlist->obj + i; + cat->thresh[j] = obj->thresh; + cat->npix[j] = obj->fdnpix; + cat->tnpix[j] = obj->dnpix; + cat->xmin[j] = obj->xmin; + cat->xmax[j] = obj->xmax; + cat->ymin[j] = obj->ymin; + cat->ymax[j] = obj->ymax; + cat->x[j] = obj->mx; + cat->y[j] = obj->my; + cat->x2[j] = obj->mx2; + cat->y2[j] = obj->my2; + cat->xy[j] = obj->mxy; + cat->errx2[j] = obj->errx2; + cat->erry2[j] = obj->erry2; + cat->errxy[j] = obj->errxy; + + cat->a[j] = obj->a; + cat->b[j] = obj->b; + cat->theta[j] = obj->theta; + + cat->cxx[j] = obj->cxx; + cat->cyy[j] = obj->cyy; + cat->cxy[j] = obj->cxy; + + cat->cflux[j] = obj->fdflux; /* these change names */ + cat->flux[j] = obj->dflux; + cat->cpeak[j] = obj->fdpeak; + cat->peak[j] = obj->dpeak; + + cat->xpeak[j] = obj->xpeak; + cat->ypeak[j] = obj->ypeak; + cat->xcpeak[j] = obj->xcpeak; + cat->ycpeak[j] = obj->ycpeak; + + cat->flag[j] = obj->flag; + + j++; + } + } + + if (include_pixels) { + /* count the total number of pixels */ + totnpix = 0; + for (i = 0; i < cat->nobj; i++) { + totnpix += cat->npix[i]; } - if (include_pixels) - { - /* count the total number of pixels */ - totnpix = 0; - for (i=0; inobj; i++) totnpix += cat->npix[i]; + /* allocate buffer for all objects' pixels */ + QMALLOC(cat->objectspix, int64_t, totnpix, status); - /* allocate buffer for all objects' pixels */ - QMALLOC(cat->objectspix, int64_t, totnpix, status); + /* allocate array of pointers into the above buffer */ + QMALLOC(cat->pix, int64_t *, nobj, status); - /* allocate array of pointers into the above buffer */ - QMALLOC(cat->pix, int64_t*, nobj, status); + pixel = objlist->plist; - pixel = objlist->plist; + /* for each object, fill buffer and direct object's to it */ + k = 0; /* current position in `objectspix` buffer */ + j = 0; /* output object index */ + for (i = 0; i < objlist->nobj; i++) { + obj = objlist->obj + i; /* input object */ + if ((survives == NULL) || survives[i]) { + /* point this object's pixel list into the buffer. */ + cat->pix[j] = cat->objectspix + k; - /* for each object, fill buffer and direct object's to it */ - k = 0; /* current position in `objectspix` buffer */ - j = 0; /* output object index */ - for (i=0; inobj; i++) + /* fill the actual pixel values */ + for (pixt = pixel + obj->firstpix; pixt >= pixel; + pixt = pixel + PLIST(pixt, nextpix), k++) { - obj = objlist->obj + i; /* input object */ - if ((survives == NULL) || survives[i]) - { - /* point this object's pixel list into the buffer. */ - cat->pix[j] = cat->objectspix + k; - - /* fill the actual pixel values */ - for (pixt=pixel+obj->firstpix; pixt>=pixel; - pixt=pixel+PLIST(pixt,nextpix), k++) - { - cat->objectspix[k] = PLIST(pixt,x) + w*PLIST(pixt,y); - } - j++; - } + cat->objectspix[k] = PLIST(pixt, x) + w * PLIST(pixt, y); } + j++; + } } + } - exit: - if (status != RETURN_OK) +exit: + if (status != RETURN_OK) { free_catalog_fields(cat); + } return status; } -void sep_catalog_free(sep_catalog *catalog) -{ - if (catalog != NULL) +void sep_catalog_free(sep_catalog * catalog) { + if (catalog != NULL) { free_catalog_fields(catalog); + } free(catalog); } diff --git a/src/extract.h b/src/extract.h index 5df3268..7303626 100644 --- a/src/extract.h +++ b/src/extract.h @@ -1,170 +1,177 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include "sepcore.h" -#define UNKNOWN -1 /* flag for LUTZ */ -#define CLEAN_ZONE 10.0 /* zone (in sigma) to consider for processing */ -#define CLEAN_STACKSIZE 3000 /* replaces prefs.clean_stacksize */ - /* (MEMORY_OBJSTACK in sextractor inputs) */ -#define CLEAN_MARGIN 0 /* replaces prefs.cleanmargin which was set based */ - /* on stuff like apertures and vignet size */ -#define MARGIN_SCALE 2.0 /* Margin / object height */ -#define MARGIN_OFFSET 4.0 /* Margin offset (pixels) */ -#define MAXDEBAREA 3 /* max. area for deblending (must be >= 1)*/ -#define MAXPICSIZE 1048576 /* max. image size in any dimension */ +#define UNKNOWN -1 /* flag for LUTZ */ +#define CLEAN_ZONE 10.0 /* zone (in sigma) to consider for processing */ +#define CLEAN_STACKSIZE 3000 /* replaces prefs.clean_stacksize */ +/* (MEMORY_OBJSTACK in sextractor inputs) */ +#define CLEAN_MARGIN 0 /* replaces prefs.cleanmargin which was set based */ +/* on stuff like apertures and vignet size */ +#define MARGIN_SCALE 2.0 /* Margin / object height */ +#define MARGIN_OFFSET 4.0 /* Margin offset (pixels) */ +#define MAXDEBAREA 3 /* max. area for deblending (must be >= 1)*/ +#define MAXPICSIZE 1048576 /* max. image size in any dimension */ /* plist-related macros */ -#define PLIST(ptr, elem) (((pbliststruct *)(ptr))->elem) -#define PLISTEXIST(elem) (plistexist_##elem) -#define PLISTPIX(ptr, elem) (*((PIXTYPE *)((ptr)+plistoff_##elem))) +#define PLIST(ptr, elem) (((pbliststruct *)(ptr))->elem) +#define PLISTEXIST(elem) (plistexist_##elem) +#define PLISTPIX(ptr, elem) (*((PIXTYPE *)((ptr) + plistoff_##elem))) /* Extraction status */ -typedef enum {COMPLETE, INCOMPLETE, NONOBJECT, OBJECT} pixstatus; +typedef enum { + COMPLETE, + INCOMPLETE, + NONOBJECT, + OBJECT +} pixstatus; /* Temporary object parameters during extraction */ -typedef struct structinfo -{ - int64_t pixnb; /* Number of pixels included */ - int64_t firstpix; /* Pointer to first pixel of pixlist */ - int64_t lastpix; /* Pointer to last pixel of pixlist */ - short flag; /* Extraction flag */ +typedef struct structinfo { + int64_t pixnb; /* Number of pixels included */ + int64_t firstpix; /* Pointer to first pixel of pixlist */ + int64_t lastpix; /* Pointer to last pixel of pixlist */ + short flag; /* Extraction flag */ } infostruct; -typedef char pliststruct; /* Dummy type for plist */ +typedef char pliststruct; /* Dummy type for plist */ -typedef struct -{ - int64_t nextpix; - int64_t x, y; +typedef struct { + int64_t nextpix; + int64_t x, y; PIXTYPE value; } pbliststruct; /* array buffer struct */ -typedef struct -{ - const BYTE *dptr; /* pointer to original data, can be any supported type */ - int dtype; /* data type of original data */ - int64_t dw, dh; /* original data width, height */ - PIXTYPE *bptr; /* buffer pointer (self-managed memory) */ - int64_t bw, bh; /* buffer width, height (bufw can be larger than w due */ - /* to padding). */ - PIXTYPE *midline; /* "middle" line in buffer (at index bh/2) */ - PIXTYPE *lastline; /* last line in buffer */ - array_converter readline; /* function to read a data line into buffer */ - int64_t elsize; /* size in bytes of one element in original data */ - int64_t yoff; /* line index in original data corresponding to bufptr */ +typedef struct { + const BYTE * dptr; /* pointer to original data, can be any supported type */ + int dtype; /* data type of original data */ + int64_t dw, dh; /* original data width, height */ + PIXTYPE * bptr; /* buffer pointer (self-managed memory) */ + int64_t bw, bh; /* buffer width, height (bufw can be larger than w due */ + /* to padding). */ + PIXTYPE * midline; /* "middle" line in buffer (at index bh/2) */ + PIXTYPE * lastline; /* last line in buffer */ + array_converter readline; /* function to read a data line into buffer */ + int64_t elsize; /* size in bytes of one element in original data */ + int64_t yoff; /* line index in original data corresponding to bufptr */ } arraybuffer; /* globals */ extern _Thread_local int64_t plistexist_cdvalue, plistexist_thresh, plistexist_var; -extern _Thread_local int64_t plistoff_value, plistoff_cdvalue, plistoff_thresh, plistoff_var; +extern _Thread_local int64_t plistoff_value, plistoff_cdvalue, plistoff_thresh, + plistoff_var; extern _Thread_local int64_t plistsize; extern _Thread_local unsigned int randseed; -typedef struct -{ +typedef struct { /* thresholds */ - float thresh; /* detect threshold (ADU) */ - float mthresh; /* max. threshold (ADU) */ + float thresh; /* detect threshold (ADU) */ + float mthresh; /* max. threshold (ADU) */ /* # pixels */ - int64_t fdnpix; /* nb of extracted pix */ - int64_t dnpix; /* nb of pix above thresh */ - int64_t npix; /* "" in measured frame */ - int64_t nzdwpix; /* nb of zero-dweights around */ - int64_t nzwpix; /* nb of zero-weights inside */ + int64_t fdnpix; /* nb of extracted pix */ + int64_t dnpix; /* nb of pix above thresh */ + int64_t npix; /* "" in measured frame */ + int64_t nzdwpix; /* nb of zero-dweights around */ + int64_t nzwpix; /* nb of zero-weights inside */ /* position */ - int64_t xpeak, ypeak; /* pos of brightest pix */ - int64_t xcpeak,ycpeak; /* pos of brightest pix */ - double mx, my; /* barycenter */ - int64_t xmin,xmax,ymin,ymax,ycmin,ycmax; /* x,y limits */ + int64_t xpeak, ypeak; /* pos of brightest pix */ + int64_t xcpeak, ycpeak; /* pos of brightest pix */ + double mx, my; /* barycenter */ + int64_t xmin, xmax, ymin, ymax, ycmin, ycmax; /* x,y limits */ /* shape */ - double mx2,my2,mxy; /* variances and covariance */ - float a, b, theta, abcor; /* moments and angle */ - float cxx,cyy,cxy; /* ellipse parameters */ - double errx2, erry2, errxy; /* Uncertainties on the variances */ + double mx2, my2, mxy; /* variances and covariance */ + float a, b, theta, abcor; /* moments and angle */ + float cxx, cyy, cxy; /* ellipse parameters */ + double errx2, erry2, errxy; /* Uncertainties on the variances */ /* flux */ - float fdflux; /* integrated ext. flux */ - float dflux; /* integrated det. flux */ - float flux; /* integrated mes. flux */ - float fluxerr; /* integrated variance */ - PIXTYPE fdpeak; /* peak intensity (ADU) */ - PIXTYPE dpeak; /* peak intensity (ADU) */ - PIXTYPE peak; /* peak intensity (ADU) */ + float fdflux; /* integrated ext. flux */ + float dflux; /* integrated det. flux */ + float flux; /* integrated mes. flux */ + float fluxerr; /* integrated variance */ + PIXTYPE fdpeak; /* peak intensity (ADU) */ + PIXTYPE dpeak; /* peak intensity (ADU) */ + PIXTYPE peak; /* peak intensity (ADU) */ /* flags */ - short flag; /* extraction flags */ + short flag; /* extraction flags */ /* accessing individual pixels in plist*/ - int64_t firstpix; /* ptr to first pixel */ - int64_t lastpix; /* ptr to last pixel */ + int64_t firstpix; /* ptr to first pixel */ + int64_t lastpix; /* ptr to last pixel */ } objstruct; -typedef struct -{ - int64_t nobj; /* number of objects in list */ - objstruct *obj; /* pointer to the object array */ - int64_t npix; /* number of pixels in pixel-list */ - pliststruct *plist; /* pointer to the pixel-list */ - PIXTYPE thresh; /* detection threshold */ +typedef struct { + int64_t nobj; /* number of objects in list */ + objstruct * obj; /* pointer to the object array */ + int64_t npix; /* number of pixels in pixel-list */ + pliststruct * plist; /* pointer to the pixel-list */ + PIXTYPE thresh; /* detection threshold */ } objliststruct; -int analysemthresh(int objnb, objliststruct *objlist, int minarea, - PIXTYPE thresh); +int analysemthresh(int objnb, objliststruct * objlist, int minarea, PIXTYPE thresh); void preanalyse(int, objliststruct *); void analyse(int, objliststruct *, int, double); typedef struct { - infostruct *info, *store; - char *marker; - pixstatus *psstack; - int64_t *start, *end, *discan; - int64_t xmin, ymin, xmax, ymax; + infostruct *info, *store; + char * marker; + pixstatus * psstack; + int64_t *start, *end, *discan; + int64_t xmin, ymin, xmax, ymax; } lutzbuffers; -int lutzalloc(int64_t, int64_t, lutzbuffers *); +int lutzalloc(int64_t, int64_t, lutzbuffers *); void lutzfree(lutzbuffers *); -int lutz(pliststruct *plistin, - int64_t *objrootsubmap, int64_t subx, int64_t suby, int64_t subw, - objstruct *objparent, objliststruct *objlist, int minarea, - lutzbuffers *buffers); +int lutz( + pliststruct * plistin, + int64_t * objrootsubmap, + int64_t subx, + int64_t suby, + int64_t subw, + objstruct * objparent, + objliststruct * objlist, + int minarea, + lutzbuffers * buffers +); void update(infostruct *, infostruct *, pliststruct *); typedef struct { - objliststruct *objlist; - short *son, *ok; - lutzbuffers lutz; + objliststruct * objlist; + short *son, *ok; + lutzbuffers lutz; } deblendctx; -int allocdeblend(int deblend_nthresh, int64_t w, int64_t h, deblendctx *); +int allocdeblend(int deblend_nthresh, int64_t w, int64_t h, deblendctx *); void freedeblend(deblendctx *); -int deblend(objliststruct *, objliststruct *, int, double, int, deblendctx *); +int deblend(objliststruct *, objliststruct *, int, double, int, deblendctx *); /*int addobjshallow(objstruct *, objliststruct *); int rmobjshallow(int, objliststruct *); @@ -172,8 +179,22 @@ void mergeobjshallow(objstruct *, objstruct *); */ int addobjdeep(int, objliststruct *, objliststruct *); -int convolve(arraybuffer *buf, int64_t y, const float *conv, int64_t convw, int64_t convh, - PIXTYPE *out); -int matched_filter(arraybuffer *imbuf, arraybuffer *nbuf, int64_t y, - const float *conv, int64_t convw, int64_t convh, - PIXTYPE *work, PIXTYPE *out, int noise_type); +int convolve( + arraybuffer * buf, + int64_t y, + const float * conv, + int64_t convw, + int64_t convh, + PIXTYPE * out +); +int matched_filter( + arraybuffer * imbuf, + arraybuffer * nbuf, + int64_t y, + const float * conv, + int64_t convw, + int64_t convh, + PIXTYPE * work, + PIXTYPE * out, + int noise_type +); diff --git a/src/lutz.c b/src/lutz.c index 3d63b9e..a001b24 100644 --- a/src/lutz.c +++ b/src/lutz.c @@ -1,24 +1,24 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* Note: was extract.c in SExtractor. */ @@ -26,11 +26,12 @@ #include #include #include + +#include "extract.h" #include "sep.h" #include "sepcore.h" -#include "extract.h" -#define NOBJ 256 /* starting number of obj. */ +#define NOBJ 256 /* starting number of obj. */ void lutzsort(infostruct *, objliststruct *); @@ -38,18 +39,17 @@ void lutzsort(infostruct *, objliststruct *); /* Allocate once for all memory space for buffers used by lutz(). */ -int lutzalloc(int64_t width, int64_t height, lutzbuffers *buffers) -{ - int64_t *discant; +int lutzalloc(int64_t width, int64_t height, lutzbuffers * buffers) { + int64_t * discant; int64_t stacksize, i; - int status=RETURN_OK; + int status = RETURN_OK; memset(buffers, 0, sizeof(lutzbuffers)); - stacksize = width+1; + stacksize = width + 1; buffers->xmin = buffers->ymin = 0; - buffers->xmax = width-1; - buffers->ymax = height-1; + buffers->xmax = width - 1; + buffers->ymax = height - 1; QMALLOC(buffers->info, infostruct, stacksize, status); QMALLOC(buffers->store, infostruct, stacksize, status); QMALLOC(buffers->marker, char, stacksize, status); @@ -58,12 +58,13 @@ int lutzalloc(int64_t width, int64_t height, lutzbuffers *buffers) QMALLOC(buffers->end, int64_t, stacksize, status); QMALLOC(buffers->discan, int64_t, stacksize, status); discant = buffers->discan; - for (i=stacksize; i--;) + for (i = stacksize; i--;) { *(discant++) = -1; + } return status; - exit: +exit: lutzfree(buffers); return status; @@ -73,8 +74,7 @@ int lutzalloc(int64_t width, int64_t height, lutzbuffers *buffers) /* Free once for all memory space for buffers used by lutz(). */ -void lutzfree(lutzbuffers *buffers) -{ +void lutzfree(lutzbuffers * buffers) { free(buffers->discan); buffers->discan = NULL; free(buffers->info); @@ -91,37 +91,38 @@ void lutzfree(lutzbuffers *buffers) buffers->end = NULL; } -static const infostruct initinfo = { - .firstpix = -1, - .lastpix = -1 -}; +static const infostruct initinfo = {.firstpix = -1, .lastpix = -1}; /********************************** lutz *************************************/ /* C implementation of R.K LUTZ' algorithm for the extraction of 8-connected pi- xels in an image */ -int lutz(pliststruct *plistin, - int64_t *objrootsubmap, int64_t subx, int64_t suby, int64_t subw, - objstruct *objparent, objliststruct *objlist, int minarea, - lutzbuffers *buffers) -{ - infostruct curpixinfo; - objstruct *obj; - pliststruct *plist,*pixel, *plistint; - - char newmarker; - int64_t cn, co, luflag, pstop, xl,xl2,yl, - out, deb_maxarea, stx,sty,enx,eny, step, - nobjm = NOBJ, - inewsymbol, *iscan; - short trunflag; - PIXTYPE thresh; - pixstatus cs, ps; +int lutz( + pliststruct * plistin, + int64_t * objrootsubmap, + int64_t subx, + int64_t suby, + int64_t subw, + objstruct * objparent, + objliststruct * objlist, + int minarea, + lutzbuffers * buffers +) { + infostruct curpixinfo; + objstruct * obj; + pliststruct *plist, *pixel, *plistint; + + char newmarker; + int64_t cn, co, luflag, pstop, xl, xl2, yl, out, deb_maxarea, stx, sty, enx, eny, + step, nobjm = NOBJ, inewsymbol, *iscan; + short trunflag; + PIXTYPE thresh; + pixstatus cs, ps; out = RETURN_OK; - deb_maxarea = minareaxmin; sty = objparent->ymin; @@ -130,210 +131,182 @@ int lutz(pliststruct *plistin, thresh = objlist->thresh; cn = 0; - iscan = objrootsubmap + (sty-suby)*subw + (stx-subx); + iscan = objrootsubmap + (sty - suby) * subw + (stx - subx); /* As we only analyse a fraction of the map, a step occurs between lines */ - step = subw - (++enx-stx); + step = subw - (++enx - stx); eny++; /*------Allocate memory to store object data */ free(objlist->obj); - if (!(obj=objlist->obj=malloc(nobjm*sizeof(objstruct)))) - { - out = MEMORY_ALLOC_ERROR; - plist = NULL; /* To avoid gcc -Wall warnings */ - goto exit_lutz; - } + if (!(obj = objlist->obj = malloc(nobjm * sizeof(objstruct)))) { + out = MEMORY_ALLOC_ERROR; + plist = NULL; /* To avoid gcc -Wall warnings */ + goto exit_lutz; + } /*------Allocate memory for the pixel list */ free(objlist->plist); - if (!(objlist->plist - = malloc((eny-sty)*(enx-stx)*plistsize))) - { - out = MEMORY_ALLOC_ERROR; - plist = NULL; /* To avoid gcc -Wall warnings */ - goto exit_lutz; - } + if (!(objlist->plist = malloc((eny - sty) * (enx - stx) * plistsize))) { + out = MEMORY_ALLOC_ERROR; + plist = NULL; /* To avoid gcc -Wall warnings */ + goto exit_lutz; + } pixel = plist = objlist->plist; /*----------------------------------------*/ - for (xl=stx; xl<=enx; xl++) + for (xl = stx; xl <= enx; xl++) { buffers->marker[xl] = 0; + } objlist->nobj = 0; co = pstop = 0; curpixinfo.pixnb = 1; - for (yl=sty; yl<=eny; yl++, iscan += step) - { - ps = COMPLETE; - cs = NONOBJECT; - trunflag = (yl==0 || yl==buffers->ymax) ? SEP_OBJ_TRUNC : 0; - if (yl==eny) - iscan = buffers->discan; - - for (xl=stx; xl<=enx; xl++) - { - newmarker = buffers->marker[xl]; - buffers->marker[xl] = 0; - if ((inewsymbol = (xl!=enx)?*(iscan++):-1) < 0) - luflag = 0; - else - { - curpixinfo.flag = trunflag; - plistint = plistin+inewsymbol; - luflag = (PLISTPIX(plistint, cdvalue) > thresh?1:0); - } - if (luflag) - { - if (xl==0 || xl==buffers->xmax) - curpixinfo.flag |= SEP_OBJ_TRUNC; - memcpy(pixel, plistint, (size_t)plistsize); - PLIST(pixel, nextpix) = -1; - curpixinfo.lastpix = curpixinfo.firstpix = cn; - cn += plistsize; - pixel += plistsize; - - /*----------------- Start Segment -----------------------------*/ - if (cs != OBJECT) - { - cs = OBJECT; - if (ps == OBJECT) - { - if (buffers->start[co] == UNKNOWN) - { - buffers->marker[xl] = 'S'; - buffers->start[co] = xl; - } - else buffers->marker[xl] = 's'; - } - else - { - buffers->psstack[pstop++] = ps; - buffers->marker[xl] = 'S'; - buffers->start[++co] = xl; - ps = COMPLETE; - buffers->info[co] = initinfo; - } - } - } - - /*-------------------Process New Marker ---------------------------*/ - if (newmarker) - { - if (newmarker == 'S') - { - buffers->psstack[pstop++] = ps; - if (cs == NONOBJECT) - { - buffers->psstack[pstop++] = COMPLETE; - buffers->info[++co] = buffers->store[xl]; - buffers->start[co] = UNKNOWN; - } - else - update(&buffers->info[co], &buffers->store[xl], plist); - ps = OBJECT; - } - - else if (newmarker == 's') - { - if ((cs == OBJECT) && (ps == COMPLETE)) - { - pstop--; - xl2 = buffers->start[co]; - update(&buffers->info[co-1], &buffers->info[co], plist); - if (buffers->start[--co] == UNKNOWN) - buffers->start[co] = xl2; - else - buffers->marker[xl2] = 's'; - } - ps = OBJECT; - } - else if (newmarker == 'f') - ps = INCOMPLETE; - else if (newmarker == 'F') - { - ps = buffers->psstack[--pstop]; - if ((cs == NONOBJECT) && (ps == COMPLETE)) - { - if (buffers->start[co] == UNKNOWN) - { - if ((int64_t)buffers->info[co].pixnb >= deb_maxarea) - { - if (objlist->nobj>=nobjm) - if (!(obj = objlist->obj = (objstruct *) - realloc(obj, (nobjm+=nobjm/2)* - sizeof(objstruct)))) - { - out = MEMORY_ALLOC_ERROR; - goto exit_lutz; - } - lutzsort(&buffers->info[co], objlist); - } - } - else - { - buffers->marker[buffers->end[co]] = 'F'; - buffers->store[buffers->start[co]] = buffers->info[co]; - } - co--; - ps = buffers->psstack[--pstop]; - } - } - } - /* end process new marker -----------------------------------------*/ - - if (luflag) - update (&buffers->info[co],&curpixinfo, plist); - else - { - /* ----------------- End Segment ------------------------------*/ - if (cs == OBJECT) - { - cs = NONOBJECT; - if (ps != COMPLETE) - { - buffers->marker[xl] = 'f'; - buffers->end[co] = xl; - } - else - { - ps = buffers->psstack[--pstop]; - buffers->marker[xl] = 'F'; - buffers->store[buffers->start[co]] = buffers->info[co]; - co--; - } - } - } - } + for (yl = sty; yl <= eny; yl++, iscan += step) { + ps = COMPLETE; + cs = NONOBJECT; + trunflag = (yl == 0 || yl == buffers->ymax) ? SEP_OBJ_TRUNC : 0; + if (yl == eny) { + iscan = buffers->discan; } - exit_lutz: - - if (objlist->nobj && out == RETURN_OK) - { - if (!(objlist->obj= - realloc(obj, objlist->nobj*sizeof(objstruct)))) - out = MEMORY_ALLOC_ERROR; - } - else - { - free(obj); - objlist->obj = NULL; + for (xl = stx; xl <= enx; xl++) { + newmarker = buffers->marker[xl]; + buffers->marker[xl] = 0; + if ((inewsymbol = (xl != enx) ? *(iscan++) : -1) < 0) { + luflag = 0; + } else { + curpixinfo.flag = trunflag; + plistint = plistin + inewsymbol; + luflag = (PLISTPIX(plistint, cdvalue) > thresh ? 1 : 0); + } + if (luflag) { + if (xl == 0 || xl == buffers->xmax) { + curpixinfo.flag |= SEP_OBJ_TRUNC; + } + memcpy(pixel, plistint, (size_t)plistsize); + PLIST(pixel, nextpix) = -1; + curpixinfo.lastpix = curpixinfo.firstpix = cn; + cn += plistsize; + pixel += plistsize; + + /*----------------- Start Segment -----------------------------*/ + if (cs != OBJECT) { + cs = OBJECT; + if (ps == OBJECT) { + if (buffers->start[co] == UNKNOWN) { + buffers->marker[xl] = 'S'; + buffers->start[co] = xl; + } else { + buffers->marker[xl] = 's'; + } + } else { + buffers->psstack[pstop++] = ps; + buffers->marker[xl] = 'S'; + buffers->start[++co] = xl; + ps = COMPLETE; + buffers->info[co] = initinfo; + } + } + } + + /*-------------------Process New Marker ---------------------------*/ + if (newmarker) { + if (newmarker == 'S') { + buffers->psstack[pstop++] = ps; + if (cs == NONOBJECT) { + buffers->psstack[pstop++] = COMPLETE; + buffers->info[++co] = buffers->store[xl]; + buffers->start[co] = UNKNOWN; + } else { + update(&buffers->info[co], &buffers->store[xl], plist); + } + ps = OBJECT; + } + + else if (newmarker == 's') + { + if ((cs == OBJECT) && (ps == COMPLETE)) { + pstop--; + xl2 = buffers->start[co]; + update(&buffers->info[co - 1], &buffers->info[co], plist); + if (buffers->start[--co] == UNKNOWN) { + buffers->start[co] = xl2; + } else { + buffers->marker[xl2] = 's'; + } + } + ps = OBJECT; + } else if (newmarker == 'f') { + ps = INCOMPLETE; + } else if (newmarker == 'F') { + ps = buffers->psstack[--pstop]; + if ((cs == NONOBJECT) && (ps == COMPLETE)) { + if (buffers->start[co] == UNKNOWN) { + if ((int64_t)buffers->info[co].pixnb >= deb_maxarea) { + if (objlist->nobj >= nobjm) { + if (!(obj = objlist->obj = (objstruct *) + realloc(obj, (nobjm += nobjm / 2) * sizeof(objstruct)))) + { + out = MEMORY_ALLOC_ERROR; + goto exit_lutz; + } + } + lutzsort(&buffers->info[co], objlist); + } + } else { + buffers->marker[buffers->end[co]] = 'F'; + buffers->store[buffers->start[co]] = buffers->info[co]; + } + co--; + ps = buffers->psstack[--pstop]; + } + } + } + /* end process new marker -----------------------------------------*/ + + if (luflag) { + update(&buffers->info[co], &curpixinfo, plist); + } else { + /* ----------------- End Segment ------------------------------*/ + if (cs == OBJECT) { + cs = NONOBJECT; + if (ps != COMPLETE) { + buffers->marker[xl] = 'f'; + buffers->end[co] = xl; + } else { + ps = buffers->psstack[--pstop]; + buffers->marker[xl] = 'F'; + buffers->store[buffers->start[co]] = buffers->info[co]; + co--; + } + } + } } + } + +exit_lutz: - if (cn && out == RETURN_OK) - { - if (!(objlist->plist=realloc(plist,cn))) - out = MEMORY_ALLOC_ERROR; + if (objlist->nobj && out == RETURN_OK) { + if (!(objlist->obj = realloc(obj, objlist->nobj * sizeof(objstruct)))) { + out = MEMORY_ALLOC_ERROR; } - else - { - free(objlist->plist); - objlist->plist = NULL; + } else { + free(obj); + objlist->obj = NULL; + } + + if (cn && out == RETURN_OK) { + if (!(objlist->plist = realloc(plist, cn))) { + out = MEMORY_ALLOC_ERROR; } + } else { + free(objlist->plist); + objlist->plist = NULL; + } return out; } @@ -342,9 +315,8 @@ int lutz(pliststruct *plistin, /* Add an object to the object list based on info (pixel info) */ -void lutzsort(infostruct *info, objliststruct *objlist) -{ - objstruct *obj = objlist->obj+objlist->nobj; +void lutzsort(infostruct * info, objliststruct * objlist) { + objstruct * obj = objlist->obj + objlist->nobj; memset(obj, 0, (size_t)sizeof(objstruct)); obj->firstpix = info->firstpix; @@ -361,18 +333,14 @@ void lutzsort(infostruct *info, objliststruct *objlist) /* update object's properties each time one of its pixels is scanned by lutz() */ -void update(infostruct *infoptr1, infostruct *infoptr2, pliststruct *pixel) -{ +void update(infostruct * infoptr1, infostruct * infoptr2, pliststruct * pixel) { infoptr1->pixnb += infoptr2->pixnb; infoptr1->flag |= infoptr2->flag; - if (infoptr1->firstpix == -1) - { - infoptr1->firstpix = infoptr2->firstpix; - infoptr1->lastpix = infoptr2->lastpix; - } - else if (infoptr2->lastpix != -1) - { - PLIST(pixel+infoptr1->lastpix, nextpix) = infoptr2->firstpix; - infoptr1->lastpix = infoptr2->lastpix; - } + if (infoptr1->firstpix == -1) { + infoptr1->firstpix = infoptr2->firstpix; + infoptr1->lastpix = infoptr2->lastpix; + } else if (infoptr2->lastpix != -1) { + PLIST(pixel + infoptr1->lastpix, nextpix) = infoptr2->firstpix; + infoptr1->lastpix = infoptr2->lastpix; + } } diff --git a/src/overlap.h b/src/overlap.h index 70c17b3..d9673be 100644 --- a/src/overlap.h +++ b/src/overlap.h @@ -9,139 +9,141 @@ #include "sepcore.h" #if defined(_MSC_VER) - #define INLINE _inline +#define INLINE _inline #else - #define INLINE inline +#define INLINE inline #endif /* Return area of a circle arc between (x1, y1) and (x2, y2) with radius r */ /* reference: http://mathworld.wolfram.com/CircularSegment.html */ -static INLINE double area_arc(double x1, double y1, double x2, double y2, - double r) -{ +static INLINE double area_arc(double x1, double y1, double x2, double y2, double r) { double a, theta; - a = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); + a = sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); theta = 2. * asin(0.5 * a / r); return 0.5 * r * r * (theta - sin(theta)); } /* Area of a triangle defined by three verticies */ -static INLINE double area_triangle(double x1, double y1, double x2, double y2, - double x3, double y3) -{ - return 0.5 * fabs(x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2)); +static INLINE double area_triangle( + double x1, double y1, double x2, double y2, double x3, double y3 +) { + return 0.5 * fabs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)); } /* Core of circular overlap routine. * Assumes that xmax >= xmin >= 0.0, ymax >= ymin >= 0.0. * (can always modify input to conform to this). */ -static INLINE double circoverlap_core(double xmin, double ymin, - double xmax, double ymax, double r) -{ +static INLINE double circoverlap_core( + double xmin, double ymin, double xmax, double ymax, double r +) { double a, b, x1, x2, y1, y2, r2, xmin2, ymin2, xmax2, ymax2; - xmin2 = xmin*xmin; - ymin2 = ymin*ymin; - r2 = r*r; - if (xmin2 + ymin2 > r2) + xmin2 = xmin * xmin; + ymin2 = ymin * ymin; + r2 = r * r; + if (xmin2 + ymin2 > r2) { return 0.; - - xmax2 = xmax*xmax; - ymax2 = ymax*ymax; - if (xmax2 + ymax2 < r2) - return (xmax-xmin)*(ymax-ymin); - - a = xmax2 + ymin2; /* (corner 1 distance)^2 */ - b = xmin2 + ymax2; /* (corner 2 distance)^2 */ - - if (a < r2 && b < r2) - { - x1 = sqrt(r2 - ymax2); - y1 = ymax; - x2 = xmax; - y2 = sqrt(r2 - xmax2); - return ((xmax-xmin)*(ymax-ymin) - - area_triangle(x1, y1, x2, y2, xmax, ymax) + - area_arc(x1, y1, x2, y2, r)); - } - - if (a < r2) - { - x1 = xmin; - y1 = sqrt(r2 - xmin2); - x2 = xmax; - y2 = sqrt(r2 - xmax2); - return (area_arc(x1, y1, x2, y2, r) + - area_triangle(x1, y1, x1, ymin, xmax, ymin) + - area_triangle(x1, y1, x2, ymin, x2, y2)); - } - - if (b < r2) - { - x1 = sqrt(r2 - ymin2); - y1 = ymin; - x2 = sqrt(r2 - ymax2); - y2 = ymax; - return (area_arc(x1, y1, x2, y2, r) + - area_triangle(x1, y1, xmin, y1, xmin, ymax) + - area_triangle(x1, y1, xmin, y2, x2, y2)); - } + } + + xmax2 = xmax * xmax; + ymax2 = ymax * ymax; + if (xmax2 + ymax2 < r2) { + return (xmax - xmin) * (ymax - ymin); + } + + a = xmax2 + ymin2; /* (corner 1 distance)^2 */ + b = xmin2 + ymax2; /* (corner 2 distance)^2 */ + + if (a < r2 && b < r2) { + x1 = sqrt(r2 - ymax2); + y1 = ymax; + x2 = xmax; + y2 = sqrt(r2 - xmax2); + return ( + (xmax - xmin) * (ymax - ymin) - area_triangle(x1, y1, x2, y2, xmax, ymax) + + area_arc(x1, y1, x2, y2, r) + ); + } + + if (a < r2) { + x1 = xmin; + y1 = sqrt(r2 - xmin2); + x2 = xmax; + y2 = sqrt(r2 - xmax2); + return ( + area_arc(x1, y1, x2, y2, r) + area_triangle(x1, y1, x1, ymin, xmax, ymin) + + area_triangle(x1, y1, x2, ymin, x2, y2) + ); + } + + if (b < r2) { + x1 = sqrt(r2 - ymin2); + y1 = ymin; + x2 = sqrt(r2 - ymax2); + y2 = ymax; + return ( + area_arc(x1, y1, x2, y2, r) + area_triangle(x1, y1, xmin, y1, xmin, ymax) + + area_triangle(x1, y1, xmin, y2, x2, y2) + ); + } /* else */ x1 = sqrt(r2 - ymin2); y1 = ymin; x2 = xmin; y2 = sqrt(r2 - xmin2); - return (area_arc(x1, y1, x2, y2, r) + - area_triangle(x1, y1, x2, y2, xmin, ymin)); + return (area_arc(x1, y1, x2, y2, r) + area_triangle(x1, y1, x2, y2, xmin, ymin)); } - /* Area of overlap of a rectangle and a circle */ -static double circoverlap(double xmin, double ymin, double xmax, double ymax, - double r) -{ +static double circoverlap( + double xmin, double ymin, double xmax, double ymax, double r +) { /* some subroutines demand that r > 0 */ - if (r <= 0.) + if (r <= 0.) { return 0.; - - if (0. <= xmin) - { - if (0. <= ymin) - return circoverlap_core(xmin, ymin, xmax, ymax, r); - else if (0. >= ymax) - return circoverlap_core(-ymax, xmin, -ymin, xmax, r); - else - return (circoverlap(xmin, ymin, xmax, 0., r) + - circoverlap(xmin, 0., xmax, ymax, r)); + } + + if (0. <= xmin) { + if (0. <= ymin) { + return circoverlap_core(xmin, ymin, xmax, ymax, r); + } else if (0. >= ymax) { + return circoverlap_core(-ymax, xmin, -ymin, xmax, r); + } else { + return ( + circoverlap(xmin, ymin, xmax, 0., r) + circoverlap(xmin, 0., xmax, ymax, r) + ); } - else if (0. >= xmax) - { - if (0. <= ymin) - return circoverlap_core(-xmax, ymin, -xmin, ymax, r); - else if (0. >= ymax) - return circoverlap_core(-xmax, -ymax, -xmin, -ymin, r); - else - return (circoverlap(xmin, ymin, xmax, 0., r) + - circoverlap(xmin, 0., xmax, ymax, r)); + } else if (0. >= xmax) { + if (0. <= ymin) { + return circoverlap_core(-xmax, ymin, -xmin, ymax, r); + } else if (0. >= ymax) { + return circoverlap_core(-xmax, -ymax, -xmin, -ymin, r); + } else { + return ( + circoverlap(xmin, ymin, xmax, 0., r) + circoverlap(xmin, 0., xmax, ymax, r) + ); } - else - { - if (0. <= ymin) - return (circoverlap(xmin, ymin, 0., ymax, r) + - circoverlap(0., ymin, xmax, ymax, r)); - if (0. >= ymax) - return (circoverlap(xmin, ymin, 0., ymax, r) + - circoverlap(0., ymin, xmax, ymax, r)); - else - return (circoverlap(xmin, ymin, 0., 0., r) + - circoverlap(0., ymin, xmax, 0., r) + - circoverlap(xmin, 0., 0., ymax, r) + - circoverlap(0., 0., xmax, ymax, r)); + } else { + if (0. <= ymin) { + return ( + circoverlap(xmin, ymin, 0., ymax, r) + circoverlap(0., ymin, xmax, ymax, r) + ); } + if (0. >= ymax) { + return ( + circoverlap(xmin, ymin, 0., ymax, r) + circoverlap(0., ymin, xmax, ymax, r) + ); + } else { + return ( + circoverlap(xmin, ymin, 0., 0., r) + circoverlap(0., ymin, xmax, 0., r) + + circoverlap(xmin, 0., 0., ymax, r) + circoverlap(0., 0., xmax, ymax, r) + ); + } + } } /* @@ -179,27 +181,23 @@ double circoverlap_new(double dx, double dy, double r) /*****************************************************************************/ /* ellipse overlap functions */ -typedef struct -{ +typedef struct { double x, y; } point; -typedef struct -{ +typedef struct { point p1, p2; } intersections; -static INLINE void swap(double *a, double *b) -{ +static INLINE void swap(double * a, double * b) { double temp; temp = *a; *a = *b; *b = temp; } -static INLINE void swap_point(point *a, point *b) -{ +static INLINE void swap_point(point * a, point * b) { point temp; temp = *a; *a = *b; @@ -207,8 +205,7 @@ static INLINE void swap_point(point *a, point *b) } /* rotate values to the left: a=b, b=c, c=a */ -static INLINE void rotate(double *a, double *b, double *c) -{ +static INLINE void rotate(double * a, double * b, double * c) { double temp; temp = *a; *a = *b; @@ -217,23 +214,22 @@ static INLINE void rotate(double *a, double *b, double *c) } /* Check if a point (x,y) is inside a triangle */ -static int in_triangle(double x, double y, double x1, double y1, - double x2, double y2, double x3, double y3) -{ +static int in_triangle( + double x, double y, double x1, double y1, double x2, double y2, double x3, double y3 +) { int c; c = 0; - c += ((y1 > y) != (y2 > y)) && (x < (x2-x1) * (y-y1) / (y2-y1) + x1); - c += ((y2 > y) != (y3 > y)) && (x < (x3-x2) * (y-y2) / (y3-y2) + x2); - c += ((y3 > y) != (y1 > y)) && (x < (x1-x3) * (y-y3) / (y1-y3) + x3); + c += ((y1 > y) != (y2 > y)) && (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1); + c += ((y2 > y) != (y3 > y)) && (x < (x3 - x2) * (y - y2) / (y3 - y2) + x2); + c += ((y3 > y) != (y1 > y)) && (x < (x1 - x3) * (y - y3) / (y1 - y3) + x3); return c % 2 == 1; } /* Intersection of a line defined by two points with a unit circle */ -static intersections circle_line(double x1, double y1, double x2, double y2) -{ +static intersections circle_line(double x1, double y1, double x2, double y2) { double a, b, delta, dx, dy; double tol = 1.e-10; intersections inter; @@ -241,70 +237,58 @@ static intersections circle_line(double x1, double y1, double x2, double y2) dx = x2 - x1; dy = y2 - y1; - if (fabs(dx) < tol && fabs(dy) < tol) - { + if (fabs(dx) < tol && fabs(dy) < tol) { + inter.p1.x = 2.; + inter.p1.y = 2.; + inter.p2.x = 2.; + inter.p2.y = 2.; + } else if (fabs(dx) > fabs(dy)) { + /* Find the slope and intercept of the line */ + a = dy / dx; + b = y1 - a * x1; + + /* Find the determinant of the quadratic equation */ + delta = 1. + a * a - b * b; + if (delta > 0.) /* solutions exist */ { + delta = sqrt(delta); + + inter.p1.x = (-a * b - delta) / (1. + a * a); + inter.p1.y = a * inter.p1.x + b; + inter.p2.x = (-a * b + delta) / (1. + a * a); + inter.p2.y = a * inter.p2.x + b; + } else /* no solution, return values > 1 */ { inter.p1.x = 2.; inter.p1.y = 2.; inter.p2.x = 2.; inter.p2.y = 2.; } - else if (fabs(dx) > fabs(dy)) - { - /* Find the slope and intercept of the line */ - a = dy / dx; - b = y1 - a * x1; - - /* Find the determinant of the quadratic equation */ - delta = 1. + a*a - b*b; - if (delta > 0.) /* solutions exist */ - { - delta = sqrt(delta); - - inter.p1.x = (-a*b - delta) / (1. + a*a); - inter.p1.y = a * inter.p1.x + b; - inter.p2.x = (-a*b + delta) / (1. + a*a); - inter.p2.y = a * inter.p2.x + b; - } - else /* no solution, return values > 1 */ - { - inter.p1.x = 2.; - inter.p1.y = 2.; - inter.p2.x = 2.; - inter.p2.y = 2.; - } - } - else - { - /* Find the slope and intercept of the line */ - a = dx / dy; - b = x1 - a * y1; - - /* Find the determinant of the quadratic equation */ - delta = 1. + a*a - b*b; - if (delta > 0.) /* solutions exist */ - { - delta = sqrt(delta); - inter.p1.y = (-a*b - delta) / (1. + a*a); - inter.p1.x = a * inter.p1.y + b; - inter.p2.y = (-a*b + delta) / (1. + a*a); - inter.p2.x = a * inter.p2.y + b; - } - else /* no solution, return values > 1 */ - { - inter.p1.x = 2.; - inter.p1.y = 2.; - inter.p2.x = 2.; - inter.p2.y = 2.; - } + } else { + /* Find the slope and intercept of the line */ + a = dx / dy; + b = x1 - a * y1; + + /* Find the determinant of the quadratic equation */ + delta = 1. + a * a - b * b; + if (delta > 0.) /* solutions exist */ { + delta = sqrt(delta); + inter.p1.y = (-a * b - delta) / (1. + a * a); + inter.p1.x = a * inter.p1.y + b; + inter.p2.y = (-a * b + delta) / (1. + a * a); + inter.p2.x = a * inter.p2.y + b; + } else /* no solution, return values > 1 */ { + inter.p1.x = 2.; + inter.p1.y = 2.; + inter.p2.x = 2.; + inter.p2.y = 2.; } + } return inter; } /* The intersection of a line with the unit circle. The intersection the * closest to (x2, y2) is chosen. */ -static point circle_segment_single2(double x1, double y1, double x2, double y2) -{ +static point circle_segment_single2(double x1, double y1, double x2, double y2) { double dx1, dy1, dx2, dy2; intersections inter; point pt1, pt2, pt; @@ -319,18 +303,18 @@ static point circle_segment_single2(double x1, double y1, double x2, double y2) dx2 = fabs(pt2.x - x2); dy2 = fabs(pt2.y - y2); - if (dx1 > dy1) /* compare based on x-axis */ + if (dx1 > dy1) /* compare based on x-axis */ { pt = (dx1 > dx2) ? pt2 : pt1; - else + } else { pt = (dy1 > dy2) ? pt2 : pt1; + } return pt; } /* Intersection(s) of a segment with the unit circle. Discard any solution not on the segment. */ -inline intersections circle_segment(double x1, double y1, double x2, double y2) -{ +inline intersections circle_segment(double x1, double y1, double x2, double y2) { intersections inter, inter_new; point pt1, pt2; @@ -338,39 +322,35 @@ inline intersections circle_segment(double x1, double y1, double x2, double y2) pt1 = inter.p1; pt2 = inter.p2; - if ((pt1.x > x1 && pt1.x > x2) || (pt1.x < x1 && pt1.x < x2) || - (pt1.y > y1 && pt1.y > y2) || (pt1.y < y1 && pt1.y < y2)) - { - pt1.x = 2.; - pt1.y = 2.; - } - if ((pt2.x > x1 && pt2.x > x2) || (pt2.x < x1 && pt2.x < x2) || - (pt2.y > y1 && pt2.y > y2) || (pt2.y < y1 && pt2.y < y2)) - { - pt2.x = 2.; - pt2.y = 2.; - } - - if (pt1.x > 1. && pt2.x < 2.) - { - inter_new.p1 = pt1; - inter_new.p2 = pt2; - } - else - { - inter_new.p1 = pt2; - inter_new.p2 = pt1; - } + if ((pt1.x > x1 && pt1.x > x2) || (pt1.x < x1 && pt1.x < x2) + || (pt1.y > y1 && pt1.y > y2) || (pt1.y < y1 && pt1.y < y2)) + { + pt1.x = 2.; + pt1.y = 2.; + } + if ((pt2.x > x1 && pt2.x > x2) || (pt2.x < x1 && pt2.x < x2) + || (pt2.y > y1 && pt2.y > y2) || (pt2.y < y1 && pt2.y < y2)) + { + pt2.x = 2.; + pt2.y = 2.; + } + + if (pt1.x > 1. && pt2.x < 2.) { + inter_new.p1 = pt1; + inter_new.p2 = pt2; + } else { + inter_new.p1 = pt2; + inter_new.p2 = pt1; + } return inter_new; } /* Given a triangle defined by three points (x1, y1), (x2, y2), and (x3, y3), find the area of overlap with the unit circle. */ -static double triangle_unitcircle_overlap(double x1, double y1, - double x2, double y2, - double x3, double y3) -{ +static double triangle_unitcircle_overlap( + double x1, double y1, double x2, double y2, double x3, double y3 +) { double d1, d2, d3, area, xp, yp; int in1, in2, in3, on1, on2, on3; int intersect13, intersect23; @@ -378,49 +358,37 @@ static double triangle_unitcircle_overlap(double x1, double y1, point pt1, pt2, pt3, pt4, pt5, pt6; /* Find distance of all vertices to circle center */ - d1 = x1*x1 + y1*y1; - d2 = x2*x2 + y2*y2; - d3 = x3*x3 + y3*y3; + d1 = x1 * x1 + y1 * y1; + d2 = x2 * x2 + y2 * y2; + d3 = x3 * x3 + y3 * y3; /* Order vertices by distance from origin */ - if (d1 < d2) - { - if (d2 < d3) - {} - else if (d1 < d3) - { - swap(&x2, &x3); - swap(&y2, &y3); - swap(&d2, &d3); - } - else - { - rotate(&x1, &x3, &x2); - rotate(&y1, &y3, &y2); - rotate(&d1, &d3, &d2); - } + if (d1 < d2) { + if (d2 < d3) { + } else if (d1 < d3) { + swap(&x2, &x3); + swap(&y2, &y3); + swap(&d2, &d3); + } else { + rotate(&x1, &x3, &x2); + rotate(&y1, &y3, &y2); + rotate(&d1, &d3, &d2); } - else - { - if (d1 < d3) - { - swap(&x1, &x2); - swap(&y1, &y2); - swap(&d1, &d2); - } - else if (d2 < d3) - { - rotate(&x1, &x2, &x3); - rotate(&y1, &y2, &y3); - rotate(&d1, &d2, &d3); - } - else - { - swap(&x1, &x3); - swap(&y1, &y3); - swap(&d1, &d3); - } + } else { + if (d1 < d3) { + swap(&x1, &x2); + swap(&y1, &y2); + swap(&d1, &d2); + } else if (d2 < d3) { + rotate(&x1, &x2, &x3); + rotate(&y1, &y2, &y3); + rotate(&d1, &d2, &d3); + } else { + swap(&x1, &x3); + swap(&y1, &y3); + swap(&d1, &d3); } + } /* Determine number of vertices inside circle */ in1 = d1 < 1.; @@ -432,131 +400,123 @@ static double triangle_unitcircle_overlap(double x1, double y1, on2 = fabs(d2 - 1.) < 1.e-10; on3 = fabs(d3 - 1.) < 1.e-10; - if (on3 || in3) /* triangle completely within circle */ + if (on3 || in3) /* triangle completely within circle */ { area = area_triangle(x1, y1, x2, y2, x3, y3); + } else if (in2 || on2) - { - /* If vertex 1 or 2 are on the edge of the circle, then we use - * the dot product to vertex 3 to determine whether an - * intersection takes place. */ - intersect13 = !on1 || (x1*(x3-x1) + y1*(y3-y1) < 0.); - intersect23 = !on2 || (x2*(x3-x2) + y2*(y3-y2) < 0.); - if (intersect13 && intersect23) - { - pt1 = circle_segment_single2(x1, y1, x3, y3); - pt2 = circle_segment_single2(x2, y2, x3, y3); - area = (area_triangle(x1, y1, x2, y2, pt1.x, pt1.y) + - area_triangle(x2, y2, pt1.x, pt1.y, pt2.x, pt2.y) + - area_arc(pt1.x, pt1.y, pt2.x, pt2.y, 1.)); - } - else if (intersect13) - { - pt1 = circle_segment_single2(x1, y1, x3, y3); - area = (area_triangle(x1, y1, x2, y2, pt1.x, pt1.y) + - area_arc(x2, y2, pt1.x, pt1.y, 1.)); - } - else if (intersect23) - { - pt2 = circle_segment_single2(x2, y2, x3, y3); - area = (area_triangle(x1, y1, x2, y2, pt2.x, pt2.y) + - area_arc(x1, y1, pt2.x, pt2.y, 1.)); - } - else - area = area_arc(x1, y1, x2, y2, 1.); + { + /* If vertex 1 or 2 are on the edge of the circle, then we use + * the dot product to vertex 3 to determine whether an + * intersection takes place. */ + intersect13 = !on1 || (x1 * (x3 - x1) + y1 * (y3 - y1) < 0.); + intersect23 = !on2 || (x2 * (x3 - x2) + y2 * (y3 - y2) < 0.); + if (intersect13 && intersect23) { + pt1 = circle_segment_single2(x1, y1, x3, y3); + pt2 = circle_segment_single2(x2, y2, x3, y3); + area = + (area_triangle(x1, y1, x2, y2, pt1.x, pt1.y) + + area_triangle(x2, y2, pt1.x, pt1.y, pt2.x, pt2.y) + + area_arc(pt1.x, pt1.y, pt2.x, pt2.y, 1.)); + } else if (intersect13) { + pt1 = circle_segment_single2(x1, y1, x3, y3); + area = + (area_triangle(x1, y1, x2, y2, pt1.x, pt1.y) + + area_arc(x2, y2, pt1.x, pt1.y, 1.)); + } else if (intersect23) { + pt2 = circle_segment_single2(x2, y2, x3, y3); + area = + (area_triangle(x1, y1, x2, y2, pt2.x, pt2.y) + + area_arc(x1, y1, pt2.x, pt2.y, 1.)); + } else { + area = area_arc(x1, y1, x2, y2, 1.); } - else if (in1) - { - /* Check for intersections of far side with circle */ - inter = circle_segment(x2, y2, x3, y3); - pt1 = inter.p1; - pt2 = inter.p2; - pt3 = circle_segment_single2(x1, y1, x2, y2); - pt4 = circle_segment_single2(x1, y1, x3, y3); - - if (pt1.x > 1.) /* indicates no intersection */ - { - /* check if the pixel vertex (x1, y2) and the origin are on - * different sides of the circle segment. If they are, the - * circle segment spans more than pi radians. - * We use the formula (y-y1) * (x2-x1) > (y2-y1) * (x-x1) - * to determine if (x, y) is on the left of the directed - * line segment from (x1, y1) to (x2, y2) */ - if (((0.-pt3.y) * (pt4.x-pt3.x) > (pt4.y-pt3.y) * (0.-pt3.x)) != - ((y1-pt3.y) * (pt4.x-pt3.x) > (pt4.y-pt3.y) * (x1-pt3.x))) - { - area = (area_triangle(x1, y1, pt3.x, pt3.y, pt4.x, pt4.y) + - PI - area_arc(pt3.x, pt3.y, pt4.x, pt4.y, 1.)); - } - else - { - area = (area_triangle(x1, y1, pt3.x, pt3.y, pt4.x, pt4.y) + - area_arc(pt3.x, pt3.y, pt4.x, pt4.y, 1.)); - } - } - else - { - /* ensure that pt1 is the point closest to (x2, y2) */ - if (((pt2.x-x2)*(pt2.x-x2) + (pt2.y-y2)*(pt2.y-y2)) < - ((pt1.x-x2)*(pt1.x-x2) + (pt1.y-y2)*(pt1.y-y2))) - swap_point(&pt1, &pt2); - - area = (area_triangle(x1, y1, pt3.x, pt3.y, pt1.x, pt1.y) + - area_triangle(x1, y1, pt1.x, pt1.y, pt2.x, pt2.y) + - area_triangle(x1, y1, pt2.x, pt2.y, pt4.x, pt4.y) + - area_arc(pt1.x, pt1.y, pt3.x, pt3.y, 1.) + - area_arc(pt2.x, pt2.y, pt4.x, pt4.y, 1.)); - } + } else if (in1) { + /* Check for intersections of far side with circle */ + inter = circle_segment(x2, y2, x3, y3); + pt1 = inter.p1; + pt2 = inter.p2; + pt3 = circle_segment_single2(x1, y1, x2, y2); + pt4 = circle_segment_single2(x1, y1, x3, y3); + + if (pt1.x > 1.) /* indicates no intersection */ { + /* check if the pixel vertex (x1, y2) and the origin are on + * different sides of the circle segment. If they are, the + * circle segment spans more than pi radians. + * We use the formula (y-y1) * (x2-x1) > (y2-y1) * (x-x1) + * to determine if (x, y) is on the left of the directed + * line segment from (x1, y1) to (x2, y2) */ + if (((0. - pt3.y) * (pt4.x - pt3.x) > (pt4.y - pt3.y) * (0. - pt3.x)) + != ((y1 - pt3.y) * (pt4.x - pt3.x) > (pt4.y - pt3.y) * (x1 - pt3.x))) + { + area = + (area_triangle(x1, y1, pt3.x, pt3.y, pt4.x, pt4.y) + PI + - area_arc(pt3.x, pt3.y, pt4.x, pt4.y, 1.)); + } else { + area = + (area_triangle(x1, y1, pt3.x, pt3.y, pt4.x, pt4.y) + + area_arc(pt3.x, pt3.y, pt4.x, pt4.y, 1.)); + } + } else { + /* ensure that pt1 is the point closest to (x2, y2) */ + if (((pt2.x - x2) * (pt2.x - x2) + (pt2.y - y2) * (pt2.y - y2)) + < ((pt1.x - x2) * (pt1.x - x2) + (pt1.y - y2) * (pt1.y - y2))) + { + swap_point(&pt1, &pt2); + } + + area = + (area_triangle(x1, y1, pt3.x, pt3.y, pt1.x, pt1.y) + + area_triangle(x1, y1, pt1.x, pt1.y, pt2.x, pt2.y) + + area_triangle(x1, y1, pt2.x, pt2.y, pt4.x, pt4.y) + + area_arc(pt1.x, pt1.y, pt3.x, pt3.y, 1.) + + area_arc(pt2.x, pt2.y, pt4.x, pt4.y, 1.)); } - else - { - inter = circle_segment(x1, y1, x2, y2); - pt1 = inter.p1; - pt2 = inter.p2; - inter = circle_segment(x2, y2, x3, y3); - pt3 = inter.p1; - pt4 = inter.p2; - inter = circle_segment(x3, y3, x1, y1); - pt5 = inter.p1; - pt6 = inter.p2; - if (pt1.x <= 1.) - { - xp = 0.5 * (pt1.x + pt2.x); - yp = 0.5 * (pt1.y + pt2.y); - area = (triangle_unitcircle_overlap(x1, y1, x3, y3, xp, yp) + - triangle_unitcircle_overlap(x2, y2, x3, y3, xp, yp)); - } - else if (pt3.x <= 1.) - { - xp = 0.5 * (pt3.x + pt4.x); - yp = 0.5 * (pt3.y + pt4.y); - area = (triangle_unitcircle_overlap(x3, y3, x1, y1, xp, yp) + - triangle_unitcircle_overlap(x2, y2, x1, y1, xp, yp)); - } - else if (pt5.x <= 1.) - { - xp = 0.5 * (pt5.x + pt6.x); - yp = 0.5 * (pt5.y + pt6.y); - area = (triangle_unitcircle_overlap(x1, y1, x2, y2, xp, yp) + - triangle_unitcircle_overlap(x3, y3, x2, y2, xp, yp)); - } - else /* no intersections */ - { - if (in_triangle(0., 0., x1, y1, x2, y2, x3, y3)) - return PI; - else - return 0.; - } + } else { + inter = circle_segment(x1, y1, x2, y2); + pt1 = inter.p1; + pt2 = inter.p2; + inter = circle_segment(x2, y2, x3, y3); + pt3 = inter.p1; + pt4 = inter.p2; + inter = circle_segment(x3, y3, x1, y1); + pt5 = inter.p1; + pt6 = inter.p2; + if (pt1.x <= 1.) { + xp = 0.5 * (pt1.x + pt2.x); + yp = 0.5 * (pt1.y + pt2.y); + area = + (triangle_unitcircle_overlap(x1, y1, x3, y3, xp, yp) + + triangle_unitcircle_overlap(x2, y2, x3, y3, xp, yp)); + } else if (pt3.x <= 1.) { + xp = 0.5 * (pt3.x + pt4.x); + yp = 0.5 * (pt3.y + pt4.y); + area = + (triangle_unitcircle_overlap(x3, y3, x1, y1, xp, yp) + + triangle_unitcircle_overlap(x2, y2, x1, y1, xp, yp)); + } else if (pt5.x <= 1.) { + xp = 0.5 * (pt5.x + pt6.x); + yp = 0.5 * (pt5.y + pt6.y); + area = + (triangle_unitcircle_overlap(x1, y1, x2, y2, xp, yp) + + triangle_unitcircle_overlap(x3, y3, x2, y2, xp, yp)); + } else /* no intersections */ { + if (in_triangle(0., 0., x1, y1, x2, y2, x3, y3)) { + return PI; + } else { + return 0.; + } } + } return area; } /* exact overlap between a rectangle defined by (xmin, ymin, xmax, ymax) and an ellipse with major and minor axes rx and ry respectively and position angle theta. */ -static double ellipoverlap(double xmin, double ymin, double xmax, double ymax, - double a, double b, double theta) -{ +static double ellipoverlap( + double xmin, double ymin, double xmax, double ymax, double a, double b, double theta +) { double cos_m_theta, sin_m_theta, scale; double x1, y1, x2, y2, x3, y3, x4, y4; @@ -578,6 +538,7 @@ static double ellipoverlap(double xmin, double ymin, double xmax, double ymax, /* Divide resulting quadrilateral into two triangles and find intersection with unit circle */ - return scale * (triangle_unitcircle_overlap(x1, y1, x2, y2, x3, y3) + - triangle_unitcircle_overlap(x1, y1, x4, y4, x3, y3)); + return scale + * (triangle_unitcircle_overlap(x1, y1, x2, y2, x3, y3) + + triangle_unitcircle_overlap(x1, y1, x4, y4, x3, y3)); } diff --git a/src/sep.h b/src/sep.h index 4d2bd56..746e8e5 100644 --- a/src/sep.h +++ b/src/sep.h @@ -1,27 +1,27 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ -#include #include +#include #ifdef _MSC_VER #define SEP_API __declspec(dllexport) @@ -30,35 +30,35 @@ #endif /* datatype codes */ -#define SEP_TBYTE 11 /* 8-bit unsigned byte */ -#define SEP_TINT 31 /* native int type */ -#define SEP_TFLOAT 42 -#define SEP_TDOUBLE 82 +#define SEP_TBYTE 11 /* 8-bit unsigned byte */ +#define SEP_TINT 31 /* native int type */ +#define SEP_TFLOAT 42 +#define SEP_TDOUBLE 82 /* object & aperture flags */ -#define SEP_OBJ_MERGED 0x0001 /* object is result of deblending */ -#define SEP_OBJ_TRUNC 0x0002 /* object truncated at image boundary */ -#define SEP_OBJ_DOVERFLOW 0x0004 /* not currently used, but could be */ -#define SEP_OBJ_SINGU 0x0008 /* x,y fully correlated */ -#define SEP_APER_TRUNC 0x0010 -#define SEP_APER_HASMASKED 0x0020 -#define SEP_APER_ALLMASKED 0x0040 +#define SEP_OBJ_MERGED 0x0001 /* object is result of deblending */ +#define SEP_OBJ_TRUNC 0x0002 /* object truncated at image boundary */ +#define SEP_OBJ_DOVERFLOW 0x0004 /* not currently used, but could be */ +#define SEP_OBJ_SINGU 0x0008 /* x,y fully correlated */ +#define SEP_APER_TRUNC 0x0010 +#define SEP_APER_HASMASKED 0x0020 +#define SEP_APER_ALLMASKED 0x0040 #define SEP_APER_NONPOSITIVE 0x0080 /* noise_type values in sep_image */ -#define SEP_NOISE_NONE 0 +#define SEP_NOISE_NONE 0 #define SEP_NOISE_STDDEV 1 -#define SEP_NOISE_VAR 2 +#define SEP_NOISE_VAR 2 /* input flags for aperture photometry */ -#define SEP_MASK_IGNORE 0x0004 +#define SEP_MASK_IGNORE 0x0004 /* threshold interpretation for sep_extract */ -#define SEP_THRESH_REL 0 /* in units of standard deviations (sigma) */ -#define SEP_THRESH_ABS 1 /* absolute data values */ +#define SEP_THRESH_REL 0 /* in units of standard deviations (sigma) */ +#define SEP_THRESH_ABS 1 /* absolute data values */ /* filter types for sep_extract */ -#define SEP_FILTER_CONV 0 +#define SEP_FILTER_CONV 0 #define SEP_FILTER_MATCHED 1 /* structs ------------------------------------------------------------------*/ @@ -69,22 +69,22 @@ * gain. */ typedef struct { - const void *data; /* data array */ - const void *noise; /* noise array (can be NULL) */ - const void *mask; /* mask array (can be NULL) */ - const void *segmap;/* segmap array (can be NULL) */ - int dtype; /* element type of image */ - int ndtype; /* element type of noise */ - int mdtype; /* element type of mask */ - int sdtype; /* element type of segmap */ - int64_t *segids; /* unique ids in segmap */ - int64_t *idcounts; /* counts of unique ids in segmap */ - int64_t numids; /* total number of unique ids in segmap */ - int64_t w; /* array width */ - int64_t h; /* array height */ - double noiseval; /* scalar noise value; used only if noise == NULL */ - short noise_type; /* interpretation of noise value */ - double gain; /* (poisson counts / data unit) */ + const void * data; /* data array */ + const void * noise; /* noise array (can be NULL) */ + const void * mask; /* mask array (can be NULL) */ + const void * segmap; /* segmap array (can be NULL) */ + int dtype; /* element type of image */ + int ndtype; /* element type of noise */ + int mdtype; /* element type of mask */ + int sdtype; /* element type of segmap */ + int64_t * segids; /* unique ids in segmap */ + int64_t * idcounts; /* counts of unique ids in segmap */ + int64_t numids; /* total number of unique ids in segmap */ + int64_t w; /* array width */ + int64_t h; /* array height */ + double noiseval; /* scalar noise value; used only if noise == NULL */ + short noise_type; /* interpretation of noise value */ + double gain; /* (poisson counts / data unit) */ double maskthresh; /* pixel considered masked if mask > maskthresh */ } sep_image; @@ -94,16 +94,16 @@ typedef struct { * and its noise with splines. */ typedef struct { - int64_t w, h; /* original image width, height */ - int64_t bw, bh; /* single tile width, height */ - int64_t nx, ny; /* number of tiles in x, y */ - int64_t n; /* nx*ny */ - float global; /* global mean */ - float globalrms; /* global sigma */ - float *back; /* node data for interpolation */ - float *dback; - float *sigma; - float *dsigma; + int64_t w, h; /* original image width, height */ + int64_t bw, bh; /* single tile width, height */ + int64_t nx, ny; /* number of tiles in x, y */ + int64_t n; /* nx*ny */ + float global; /* global mean */ + float globalrms; /* global sigma */ + float * back; /* node data for interpolation */ + float * dback; + float * sigma; + float * dsigma; } sep_bkg; /* sep_catalog @@ -112,28 +112,28 @@ typedef struct { * one entry per detected object. */ typedef struct { - int nobj; /* number of objects (length of all arrays) */ - float *thresh; /* threshold (ADU) */ - int64_t *npix; /* # pixels extracted (size of pix array) */ - int64_t *tnpix; /* # pixels above thresh (unconvolved) */ + int nobj; /* number of objects (length of all arrays) */ + float * thresh; /* threshold (ADU) */ + int64_t * npix; /* # pixels extracted (size of pix array) */ + int64_t * tnpix; /* # pixels above thresh (unconvolved) */ int64_t *xmin, *xmax; int64_t *ymin, *ymax; - double *x, *y; /* barycenter (first moments) */ - double *x2, *y2, *xy; /* second moments */ - double *errx2, *erry2, *errxy; /* second moment errors */ - float *a, *b, *theta; /* ellipse parameters */ - float *cxx, *cyy, *cxy; /* ellipse parameters (alternative) */ - float *cflux; /* total flux of pixels (convolved im) */ - float *flux; /* total flux of pixels (unconvolved) */ - float *cpeak; /* peak intensity (ADU) (convolved) */ - float *peak; /* peak intensity (ADU) (unconvolved) */ - int64_t *xcpeak, *ycpeak; /* x, y coords of peak (convolved) pixel */ - int64_t *xpeak, *ypeak; /* x, y coords of peak (unconvolved) pixel */ - short *flag; /* extraction flags */ - int64_t **pix; /* array giving indicies of object's pixels in */ - /* image (linearly indexed). Length is `npix`. */ - /* (pointer to within the `objectspix` buffer) */ - int64_t *objectspix; /* buffer holding pixel indicies for all objects */ + double *x, *y; /* barycenter (first moments) */ + double *x2, *y2, *xy; /* second moments */ + double *errx2, *erry2, *errxy; /* second moment errors */ + float *a, *b, *theta; /* ellipse parameters */ + float *cxx, *cyy, *cxy; /* ellipse parameters (alternative) */ + float * cflux; /* total flux of pixels (convolved im) */ + float * flux; /* total flux of pixels (unconvolved) */ + float * cpeak; /* peak intensity (ADU) (convolved) */ + float * peak; /* peak intensity (ADU) (unconvolved) */ + int64_t *xcpeak, *ycpeak; /* x, y coords of peak (convolved) pixel */ + int64_t *xpeak, *ypeak; /* x, y coords of peak (unconvolved) pixel */ + short * flag; /* extraction flags */ + int64_t ** pix; /* array giving indicies of object's pixels in */ + /* image (linearly indexed). Length is `npix`. */ + /* (pointer to within the `objectspix` buffer) */ + int64_t * objectspix; /* buffer holding pixel indicies for all objects */ } sep_catalog; @@ -155,19 +155,23 @@ typedef struct { * - fw, fh = (3, 3) * - fthresh = 0.0 */ -SEP_API int sep_background(const sep_image *image, - int64_t bw, int64_t bh, /* size of a single background tile */ - int64_t fw, int64_t fh, /* filter size in tiles */ - double fthresh, /* filter threshold */ - sep_bkg **bkg); /* OUTPUT */ +SEP_API int sep_background( + const sep_image * image, + int64_t bw, + int64_t bh, /* size of a single background tile */ + int64_t fw, + int64_t fh, /* filter size in tiles */ + double fthresh, /* filter threshold */ + sep_bkg ** bkg +); /* OUTPUT */ /* sep_bkg_global[rms]() * * Get the estimate of the global background "median" or standard deviation. */ -SEP_API float sep_bkg_global(const sep_bkg *bkg); -SEP_API float sep_bkg_globalrms(const sep_bkg *bkg); +SEP_API float sep_bkg_global(const sep_bkg * bkg); +SEP_API float sep_bkg_globalrms(const sep_bkg * bkg); /* sep_bkg_pix() @@ -175,7 +179,7 @@ SEP_API float sep_bkg_globalrms(const sep_bkg *bkg); * Return background at (x, y). * Unlike other routines, this uses simple linear interpolation. */ -SEP_API float sep_bkg_pix(const sep_bkg *bkg, int64_t x, int64_t y); +SEP_API float sep_bkg_pix(const sep_bkg * bkg, int64_t x, int64_t y); /* sep_bkg_[sub,rms]line() @@ -185,9 +189,9 @@ SEP_API float sep_bkg_pix(const sep_bkg *bkg, int64_t x, int64_t y); * The second function subtracts the background from the input array. * Line must be an array with same width as original image. */ -SEP_API int sep_bkg_line(const sep_bkg *bkg, int64_t y, void *line, int dtype); -SEP_API int sep_bkg_subline(const sep_bkg *bkg, int64_t y, void *line, int dtype); -SEP_API int sep_bkg_rmsline(const sep_bkg *bkg, int64_t y, void *line, int dtype); +SEP_API int sep_bkg_line(const sep_bkg * bkg, int64_t y, void * line, int dtype); +SEP_API int sep_bkg_subline(const sep_bkg * bkg, int64_t y, void * line, int dtype); +SEP_API int sep_bkg_rmsline(const sep_bkg * bkg, int64_t y, void * line, int dtype); /* sep_bkg_[sub,rms]array() @@ -197,15 +201,15 @@ SEP_API int sep_bkg_rmsline(const sep_bkg *bkg, int64_t y, void *line, int dtype * The second function subtracts the background from the input array. * `arr` must be an array of the same size as original image. */ -SEP_API int sep_bkg_array(const sep_bkg *bkg, void *arr, int dtype); -SEP_API int sep_bkg_subarray(const sep_bkg *bkg, void *arr, int dtype); -SEP_API int sep_bkg_rmsarray(const sep_bkg *bkg, void *arr, int dtype); +SEP_API int sep_bkg_array(const sep_bkg * bkg, void * arr, int dtype); +SEP_API int sep_bkg_subarray(const sep_bkg * bkg, void * arr, int dtype); +SEP_API int sep_bkg_rmsarray(const sep_bkg * bkg, void * arr, int dtype); /* sep_bkg_free() * * Free memory associated with bkg. */ -SEP_API void sep_bkg_free(sep_bkg *bkg); +SEP_API void sep_bkg_free(sep_bkg * bkg); /*-------------------------- source extraction ------------------------------*/ @@ -224,20 +228,22 @@ SEP_API void sep_bkg_free(sep_bkg *bkg); * (the absolute threshold will be thresh*noise[i,j]). * */ -SEP_API int sep_extract(const sep_image *image, - float thresh, /* detection threshold [1.5] */ - int thresh_type, /* threshold units [SEP_THRESH_REL] */ - int minarea, /* minimum area in pixels [5] */ - const float *conv, /* convolution array (can be NULL) */ - /* [{1 2 1 2 4 2 1 2 1}] */ - int64_t convw, int64_t convh, /* w, h of convolution array [3,3] */ - int filter_type, /* convolution (0) or matched (1) [0] */ - int deblend_nthresh, /* deblending thresholds [32] */ - double deblend_cont, /* min. deblending contrast [0.005] */ - int clean_flag, /* perform cleaning? [1] */ - double clean_param, /* clean parameter [1.0] */ - sep_catalog **catalog); /* OUTPUT catalog */ - +SEP_API int sep_extract( + const sep_image * image, + float thresh, /* detection threshold [1.5] */ + int thresh_type, /* threshold units [SEP_THRESH_REL] */ + int minarea, /* minimum area in pixels [5] */ + const float * conv, /* convolution array (can be NULL) */ + /* [{1 2 1 2 4 2 1 2 1}] */ + int64_t convw, + int64_t convh, /* w, h of convolution array [3,3] */ + int filter_type, /* convolution (0) or matched (1) [0] */ + int deblend_nthresh, /* deblending thresholds [32] */ + double deblend_cont, /* min. deblending contrast [0.005] */ + int clean_flag, /* perform cleaning? [1] */ + double clean_param, /* clean parameter [1.0] */ + sep_catalog ** catalog +); /* OUTPUT catalog */ /* set and get the size of the pixel stack used in extract() */ @@ -249,7 +255,7 @@ SEP_API void sep_set_sub_object_limit(int val); SEP_API int sep_get_sub_object_limit(void); /* free memory associated with a catalog */ -SEP_API void sep_catalog_free(sep_catalog *catalog); +SEP_API void sep_catalog_free(sep_catalog * catalog); /*-------------------------- aperture photometry ----------------------------*/ @@ -270,33 +276,70 @@ SEP_API void sep_catalog_free(sep_catalog *catalog); * corrected. The area can differ from the exact area of a circle due * to inexact subpixel sampling and intersection with array boundaries. */ -SEP_API int sep_sum_circle(const sep_image *image, - double x, /* center of aperture in x */ - double y, /* center of aperture in y */ - double r, /* radius of aperture */ - int id, /* optional id to test against segmap array */ - int subpix, /* subpixel sampling */ - short inflags, /* input flags (see below) */ - double *sum, /* OUTPUT: sum */ - double *sumerr, /* OUTPUT: error on sum */ - double *area, /* OUTPUT: area included in sum */ - short *flag); /* OUTPUT: flags */ - - -SEP_API int sep_sum_circann(const sep_image *image, - double x, double y, double rin, double rout, - int id, int subpix, short inflags, - double *sum, double *sumerr, double *area, short *flag); - -SEP_API int sep_sum_ellipse(const sep_image *image, - double x, double y, double a, double b, double theta, - double r, int id, int subpix, short inflags, - double *sum, double *sumerr, double *area, short *flag); - -SEP_API int sep_sum_ellipann(const sep_image *image, - double x, double y, double a, double b, double theta, - double rin, double rout, int id, int subpix, short inflags, - double *sum, double *sumerr, double *area, short *flag); +SEP_API int sep_sum_circle( + const sep_image * image, + double x, /* center of aperture in x */ + double y, /* center of aperture in y */ + double r, /* radius of aperture */ + int id, /* optional id to test against segmap array */ + int subpix, /* subpixel sampling */ + short inflags, /* input flags (see below) */ + double * sum, /* OUTPUT: sum */ + double * sumerr, /* OUTPUT: error on sum */ + double * area, /* OUTPUT: area included in sum */ + short * flag +); /* OUTPUT: flags */ + + +SEP_API int sep_sum_circann( + const sep_image * image, + double x, + double y, + double rin, + double rout, + int id, + int subpix, + short inflags, + double * sum, + double * sumerr, + double * area, + short * flag +); + +SEP_API int sep_sum_ellipse( + const sep_image * image, + double x, + double y, + double a, + double b, + double theta, + double r, + int id, + int subpix, + short inflags, + double * sum, + double * sumerr, + double * area, + short * flag +); + +SEP_API int sep_sum_ellipann( + const sep_image * image, + double x, + double y, + double a, + double b, + double theta, + double rin, + double rout, + int id, + int subpix, + short inflags, + double * sum, + double * sumerr, + double * area, + short * flag +); /* sep_sum_circann_multi() * @@ -314,11 +357,21 @@ SEP_API int sep_sum_ellipann(const sep_image *image, annulus (if mask not NULL). * flag: Output flag (non-array). */ -SEP_API int sep_sum_circann_multi(const sep_image *im, - double x, double y, double rmax, int64_t n, int id, int subpix, - short inflag, - double *sum, double *sumvar, double *area, - double *maskarea, short *flag); +SEP_API int sep_sum_circann_multi( + const sep_image * im, + double x, + double y, + double rmax, + int64_t n, + int id, + int subpix, + short inflag, + double * sum, + double * sumvar, + double * area, + double * maskarea, + short * flag +); /* sep_flux_radius() * @@ -334,10 +387,20 @@ SEP_API int sep_sum_circann_multi(const sep_image *im, * r : (output) result array of length n. * flag : (output) scalar flag */ -SEP_API int sep_flux_radius(const sep_image *im, - double x, double y, double rmax, int id, int subpix, short inflag, - const double *fluxtot, const double *fluxfrac, int64_t n, - double *r, short *flag); +SEP_API int sep_flux_radius( + const sep_image * im, + double x, + double y, + double rmax, + int id, + int subpix, + short inflag, + const double * fluxtot, + const double * fluxfrac, + int64_t n, + double * r, + short * flag +); /* sep_kron_radius() * @@ -355,9 +418,18 @@ SEP_API int sep_flux_radius(const sep_image *im, * SEP_APER_NONPOSITIVE - There was a nonpositive numerator or deminator. * kronrad = 0. */ -SEP_API int sep_kron_radius(const sep_image *im, double x, double y, - double cxx, double cyy, double cxy, double r, int id, - double *kronrad, short *flag); +SEP_API int sep_kron_radius( + const sep_image * im, + double x, + double y, + double cxx, + double cyy, + double cxy, + double r, + int id, + double * kronrad, + short * flag +); /* sep_windowed() @@ -372,9 +444,18 @@ SEP_API int sep_kron_radius(const sep_image *im, double x, double y, * xout, yout : output center. * niter : number of iterations used. */ -SEP_API int sep_windowed(const sep_image *im, - double x, double y, double sig, int subpix, short inflag, - double *xout, double *yout, int *niter, short *flag); +SEP_API int sep_windowed( + const sep_image * im, + double x, + double y, + double sig, + int subpix, + short inflag, + double * xout, + double * yout, + int * niter, + short * flag +); /* sep_set_ellipse() @@ -383,9 +464,18 @@ SEP_API int sep_windowed(const sep_image *im, * * Ellipse: cxx*(x'-x)^2 + cyy*(y'-y)^2 + cxy*(x'-x)*(y'-y) = r^2 */ -SEP_API void sep_set_ellipse(unsigned char *arr, int64_t w, int64_t h, - double x, double y, double cxx, double cyy, double cxy, - double r, unsigned char val); +SEP_API void sep_set_ellipse( + unsigned char * arr, + int64_t w, + int64_t h, + double x, + double y, + double cxx, + double cyy, + double cxy, + double r, + unsigned char val +); /* sep_ellipse_axes() @@ -400,15 +490,17 @@ SEP_API void sep_set_ellipse(unsigned char *arr, int64_t w, int64_t h, * b = semiminor axis * theta = angle in radians counter-clockwise from positive x axis */ -SEP_API int sep_ellipse_axes(double cxx, double cyy, double cxy, - double *a, double *b, double *theta); -SEP_API void sep_ellipse_coeffs(double a, double b, double theta, - double *cxx, double *cyy, double *cxy); +SEP_API int sep_ellipse_axes( + double cxx, double cyy, double cxy, double * a, double * b, double * theta +); +SEP_API void sep_ellipse_coeffs( + double a, double b, double theta, double * cxx, double * cyy, double * cxy +); /*----------------------- info & error messaging ----------------------------*/ /* sep_version_string : library version (e.g., "0.2.0") */ -SEP_API extern const char *const sep_version_string; +SEP_API extern const char * const sep_version_string; /* sep_get_errmsg() * @@ -416,7 +508,7 @@ SEP_API extern const char *const sep_version_string; * error status value. The message may be up to 60 characters long, plus * the terminating null character. */ -SEP_API void sep_get_errmsg(int status, char *errtext); +SEP_API void sep_get_errmsg(int status, char * errtext); /* sep_get_errdetail() @@ -424,4 +516,4 @@ SEP_API void sep_get_errmsg(int status, char *errtext); * Return a longer error message with more specifics about the problem. * The message may be up to 512 characters. */ -SEP_API void sep_get_errdetail(char *errtext); +SEP_API void sep_get_errdetail(char * errtext); diff --git a/src/sepcore.h b/src/sepcore.h index 3e2782c..aaf853d 100644 --- a/src/sepcore.h +++ b/src/sepcore.h @@ -1,93 +1,104 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -* SEP is free software: you can redistribute it and/or modify -* it under the terms of the GNU Lesser General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* SEP is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with SEP. If not, see . -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + * SEP is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * SEP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with SEP. If not, see . + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include -#define RETURN_OK 0 /* must be zero */ -#define MEMORY_ALLOC_ERROR 1 -#define PIXSTACK_FULL 2 -#define ILLEGAL_DTYPE 3 -#define ILLEGAL_SUBPIX 4 -#define NON_ELLIPSE_PARAMS 5 +#define RETURN_OK 0 /* must be zero */ +#define MEMORY_ALLOC_ERROR 1 +#define PIXSTACK_FULL 2 +#define ILLEGAL_DTYPE 3 +#define ILLEGAL_SUBPIX 4 +#define NON_ELLIPSE_PARAMS 5 #define ILLEGAL_APER_PARAMS 6 -#define DEBLEND_OVERFLOW 7 -#define LINE_NOT_IN_BUF 8 -#define RELTHRESH_NO_NOISE 9 -#define UNKNOWN_NOISE_TYPE 10 +#define DEBLEND_OVERFLOW 7 +#define LINE_NOT_IN_BUF 8 +#define RELTHRESH_NO_NOISE 9 +#define UNKNOWN_NOISE_TYPE 10 -#define BIG 1e+30 /* a huge number (< biggest value a float can store) */ -#define PI M_PI -#define DEG (PI/180.0) /* 1 deg in radians */ +#define BIG 1e+30 /* a huge number (< biggest value a float can store) */ +#define PI M_PI +#define DEG (PI / 180.0) /* 1 deg in radians */ -typedef int LONG; -typedef unsigned int ULONG; -typedef unsigned char BYTE; /* a byte */ +typedef int LONG; +typedef unsigned int ULONG; +typedef unsigned char BYTE; /* a byte */ /* keep these synchronized */ -typedef float PIXTYPE; /* type used inside of functions */ -#define PIXDTYPE SEP_TFLOAT /* dtype code corresponding to PIXTYPE */ +typedef float PIXTYPE; /* type used inside of functions */ +#define PIXDTYPE SEP_TFLOAT /* dtype code corresponding to PIXTYPE */ /* signature of converters */ -typedef PIXTYPE (*converter)(const void *ptr); -typedef void (*array_converter)(const void *ptr, int64_t n, PIXTYPE *target); -typedef void (*array_writer)(const float *ptr, int64_t n, void *target); +typedef PIXTYPE (*converter)(const void * ptr); +typedef void (*array_converter)(const void * ptr, int64_t n, PIXTYPE * target); +typedef void (*array_writer)(const float * ptr, int64_t n, void * target); -#define QCALLOC(ptr, typ, nel, status) \ - {if (!(ptr = (typ *)calloc((size_t)(nel),sizeof(typ)))) \ - { \ - char errtext[160]; \ - sprintf(errtext, #ptr " (" #nel "=%lu elements) " \ - "at line %d in module " __FILE__ " !", \ - (size_t)(nel)*sizeof(typ), __LINE__); \ - put_errdetail(errtext); \ - status = MEMORY_ALLOC_ERROR; \ - goto exit; \ - }; \ +#define QCALLOC(ptr, typ, nel, status) \ + { \ + if (!(ptr = (typ *)calloc((size_t)(nel), sizeof(typ)))) { \ + char errtext[160]; \ + sprintf( \ + errtext, \ + #ptr " (" #nel \ + "=%lu elements) " \ + "at line %d in module " __FILE__ " !", \ + (size_t)(nel) * sizeof(typ), \ + __LINE__ \ + ); \ + put_errdetail(errtext); \ + status = MEMORY_ALLOC_ERROR; \ + goto exit; \ + }; \ } -#define QMALLOC(ptr, typ, nel, status) \ - {if (!(ptr = malloc((size_t)(nel)*sizeof(typ)))) \ - { \ - char errtext[160]; \ - sprintf(errtext, #ptr " (" #nel "=%lu elements) " \ - "at line %d in module " __FILE__ " !", \ - (size_t)(nel)*sizeof(typ), __LINE__); \ - put_errdetail(errtext); \ - status = MEMORY_ALLOC_ERROR; \ - goto exit; \ - }; \ +#define QMALLOC(ptr, typ, nel, status) \ + { \ + if (!(ptr = malloc((size_t)(nel) * sizeof(typ)))) { \ + char errtext[160]; \ + sprintf( \ + errtext, \ + #ptr " (" #nel \ + "=%lu elements) " \ + "at line %d in module " __FILE__ " !", \ + (size_t)(nel) * sizeof(typ), \ + __LINE__ \ + ); \ + put_errdetail(errtext); \ + status = MEMORY_ALLOC_ERROR; \ + goto exit; \ + }; \ } -float fqmedian(float *ra, int64_t n); -void put_errdetail(const char *errtext); +float fqmedian(float * ra, int64_t n); +void put_errdetail(const char * errtext); -int get_converter(int dtype, converter *f, int64_t *size); -int get_array_converter(int dtype, array_converter *f, int64_t *size); -int get_array_writer(int dtype, array_writer *f, int64_t *size); -int get_array_subtractor(int dtype, array_writer *f, int64_t *size); +int get_converter(int dtype, converter * f, int64_t * size); +int get_array_converter(int dtype, array_converter * f, int64_t * size); +int get_array_writer(int dtype, array_writer * f, int64_t * size); +int get_array_subtractor(int dtype, array_writer * f, int64_t * size); #if defined(_MSC_VER) #define _Thread_local __declspec(thread) -#define _Atomic // this isn't great, but we only use atomic for global settings -#define rand_r(SEED) rand() // MSVC doesn't provide rand_r, but makes rand safe for re-entrancy +#define _Atomic // this isn't great, but we only use atomic for global settings +#define rand_r(SEED) \ + rand() // MSVC doesn't provide rand_r, but makes rand safe for re-entrancy #endif diff --git a/src/util.c b/src/util.c index 41be2a2..acd9257 100644 --- a/src/util.c +++ b/src/util.c @@ -1,152 +1,129 @@ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -* -* This file is part of SEP -* -* All content except array comparison functions fqcmp() and fqmedian() is -* distributed under an MIT license. -* -* Copyright 2014 SEP developers -* -* Array comparison functions fqcmp() and fqmedian() are distributed under an -* LGPL license: -* -* Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC -* Copyright 2014 SEP developers -* -*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + * + * This file is part of SEP + * + * All content except array comparison functions fqcmp() and fqmedian() is + * distributed under an MIT license. + * + * Copyright 2014 SEP developers + * + * Array comparison functions fqcmp() and fqmedian() are distributed under an + * LGPL license: + * + * Copyright 1993-2011 Emmanuel Bertin -- IAP/CNRS/UPMC + * Copyright 2014 SEP developers + * + *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include #include #include #include + #include "sep.h" #include "sepcore.h" #define DETAILSIZE 512 -const char *const sep_version_string = "1.3.2"; +const char * const sep_version_string = "1.3.2"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ /* data type conversion mechanics for runtime type conversion */ -PIXTYPE convert_dbl(const void *ptr) -{ +PIXTYPE convert_dbl(const void * ptr) { return *(const double *)ptr; } -PIXTYPE convert_flt(const void *ptr) -{ +PIXTYPE convert_flt(const void * ptr) { return *(const float *)ptr; } -PIXTYPE convert_int(const void *ptr) -{ +PIXTYPE convert_int(const void * ptr) { return *(const int *)ptr; } -PIXTYPE convert_byt(const void *ptr) -{ +PIXTYPE convert_byt(const void * ptr) { return *(const BYTE *)ptr; } /* return the correct converter depending on the datatype code */ -int get_converter(int dtype, converter *f, int64_t *size) -{ +int get_converter(int dtype, converter * f, int64_t * size) { int status = RETURN_OK; - if (dtype == SEP_TFLOAT) - { - *f = convert_flt; - *size = sizeof(float); - } - else if (dtype == SEP_TINT) - { - *f = convert_int; - *size = sizeof(int); - } - else if (dtype == SEP_TDOUBLE) - { - *f = convert_dbl; - *size = sizeof(double); - } - else if (dtype == SEP_TBYTE) - { - *f = convert_byt; - *size = sizeof(BYTE); - } - else - { - *f = NULL; - *size = 0; - status = ILLEGAL_DTYPE; - } + if (dtype == SEP_TFLOAT) { + *f = convert_flt; + *size = sizeof(float); + } else if (dtype == SEP_TINT) { + *f = convert_int; + *size = sizeof(int); + } else if (dtype == SEP_TDOUBLE) { + *f = convert_dbl; + *size = sizeof(double); + } else if (dtype == SEP_TBYTE) { + *f = convert_byt; + *size = sizeof(BYTE); + } else { + *f = NULL; + *size = 0; + status = ILLEGAL_DTYPE; + } return status; } /* array conversions */ -void convert_array_flt(const void *ptr, int64_t n, PIXTYPE *target) -{ - const float *source = ptr; +void convert_array_flt(const void * ptr, int64_t n, PIXTYPE * target) { + const float * source = ptr; int64_t i; - for (i=0; i*p2, 0 if *p1==*p2, -1 otherwise */ { - double f1=*((const float *)p1); - double f2=*((const float *)p2); - return f1>f2? 1 : (f1 f2 ? 1 : (f1 < f2 ? -1 : 0); } -float fqmedian(float *ra, int64_t n) +float fqmedian(float * ra, int64_t n) /* Compute median of an array of floats. * * WARNING: input data are reordered! */ { qsort(ra, n, sizeof(float), fqcmp); - if (n<2) + if (n < 2) { return *ra; - else - return n&1? ra[n/2] : (ra[n/2-1]+ra[n/2])/2.0; + } else { + return n & 1 ? ra[n / 2] : (ra[n / 2 - 1] + ra[n / 2]) / 2.0; + } } From 744679dae7723cc2214a3ac12001326366d49079 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Fri, 16 Feb 2024 14:09:45 +0100 Subject: [PATCH 30/47] Small changes to support numpy 2.0 when released. --- pyproject.toml | 10 ++++++++-- sep_pjw.pyx | 2 +- setup.py | 2 +- test.py | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6bbf11c..0ce7375 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,11 @@ [build-system] -requires = ["setuptools>=61.0", "wheel", "oldest-supported-numpy", "Cython", "setuptools_scm>=8.0"] +requires = [ + "setuptools>=61.0", + "wheel", + "numpy>=2.0.0.dev0", + "Cython", + "setuptools_scm>=8.0", +] build-backend = "setuptools.build_meta" [project] @@ -17,7 +23,7 @@ authors = [ { name="Michael Wuertenberger"}, { name="Ingvar Stepanyan"}, { name="Gabe Brammer"}, - { name="Peter Watson", email="peter.watson+sep@inaf.it" }, + { name="Peter Watson"}, ] maintainers = [ { name="Peter Watson", email="peter.watson+sep@inaf.it" }, diff --git a/sep_pjw.pyx b/sep_pjw.pyx index 4be0c47..e326828 100644 --- a/sep_pjw.pyx +++ b/sep_pjw.pyx @@ -586,7 +586,7 @@ cdef packed struct Object: np.int64_t ycpeak np.int64_t xpeak np.int64_t ypeak - np.int_t flag + long flag default_kernel = np.array([[1.0, 2.0, 1.0], [2.0, 4.0, 2.0], diff --git a/setup.py b/setup.py index 937edee..f9f7b9d 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ depends=headerfiles, define_macros=[ ("_USE_MATH_DEFINES", "1"), - ("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"), + ("NPY_NO_DEPRECATED_API", "NPY_2_0_API_VERSION"), ], ) ] diff --git a/test.py b/test.py index a2fadde..9375240 100755 --- a/test.py +++ b/test.py @@ -801,7 +801,7 @@ def test_byte_order_exception(): and aperture functions.""" data = np.ones((100, 100), dtype=np.float64) - data = data.byteswap(True).newbyteorder() + data = data.view(data.dtype.newbyteorder("S")) with pytest.raises(ValueError) as excinfo: bkg = sep.Background(data) assert "byte order" in excinfo.value.args[0] From 3983e9c0f9f65fcde0086c3c2c884e4170a2ccaa Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:24:28 +0100 Subject: [PATCH 31/47] Fix C test, couple small typos. --- ctest/test_image.c | 23 ++++++++++++++++++++--- src/lutz.c | 1 + src/overlap.h | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ctest/test_image.c b/ctest/test_image.c index 729ba55..0969266 100644 --- a/ctest/test_image.c +++ b/ctest/test_image.c @@ -94,7 +94,7 @@ void printbox(float * im, int w, int h, int xmin, int xmax, int ymin, int ymax) } /* an extremely dumb reader for our specific test FITS file! */ -int read_test_image(char * fname, float ** data, int * nx, int * ny) { +int read_test_image(char * fname, float ** data, int64_t * nx, int64_t * ny) { FILE * f; char buf[80]; /* buffer to hold a line */ long pos; @@ -197,7 +197,8 @@ void print_time(char * s, uint64_t tdiff) { int main(int argc, char ** argv) { char *fname1, *fname2; - int i, status, nx, ny; + int i, status; + int64_t nx, ny; double *flux, *fluxerr, *fluxt, *fluxerrt, *area, *areat; short *flag, *flagt; float *data, *imback; @@ -231,7 +232,23 @@ int main(int argc, char ** argv) { /* background estimation */ t0 = gettime_ns(); sep_image im = { - data, NULL, NULL, NULL, SEP_TFLOAT, 0, 0, 0, nx, ny, 0.0, SEP_NOISE_NONE, 1.0, 0.0 + data, + NULL, + NULL, + NULL, + SEP_TFLOAT, + 0, + 0, + 0, + 0, + 0, + 0, + nx, + ny, + 0.0, + SEP_NOISE_NONE, + 1.0, + 0.0 }; status = sep_background(&im, 64, 64, 3, 3, 0.0, &bkg); t1 = gettime_ns(); diff --git a/src/lutz.c b/src/lutz.c index a001b24..fe87ba0 100644 --- a/src/lutz.c +++ b/src/lutz.c @@ -164,6 +164,7 @@ int lutz( objlist->nobj = 0; co = pstop = 0; curpixinfo.pixnb = 1; + curpixinfo.flag = curpixinfo.firstpix = curpixinfo.lastpix = 0; for (yl = sty; yl <= eny; yl++, iscan += step) { ps = COMPLETE; diff --git a/src/overlap.h b/src/overlap.h index d9673be..8f459bc 100644 --- a/src/overlap.h +++ b/src/overlap.h @@ -314,7 +314,7 @@ static point circle_segment_single2(double x1, double y1, double x2, double y2) /* Intersection(s) of a segment with the unit circle. Discard any solution not on the segment. */ -inline intersections circle_segment(double x1, double y1, double x2, double y2) { +static inline intersections circle_segment(double x1, double y1, double x2, double y2) { intersections inter, inter_new; point pt1, pt2; From 8235ed46525cf35afb636519d54fdfcecb5c6e1a Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:25:41 +0100 Subject: [PATCH 32/47] Additional python tests, improved docstrings, and updated changelog. --- .pre-commit-config.yaml | 7 +- CHANGES.md | 9 +++ bench.py | 3 + ctest/compare.py | 28 +++++++ docs/conf.py | 2 + pyproject.toml | 2 +- setup.py | 2 + src/util.c | 2 +- test.py | 171 +++++++++++++++++++++++++++++++++------- 9 files changed, 194 insertions(+), 32 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7a87cd5..e616e43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: "data/*" +exclude: "^data/" repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v3.2.0 @@ -16,6 +16,7 @@ repos: rev: 23.12.1 hooks: - id: black + args: [--preview] - repo: https://github.com/numpy/numpydoc rev: v1.6.0 hooks: @@ -24,8 +25,8 @@ repos: rev: v0.9.1 hooks: - id: sphinx-lint - args: [--enable=all, --disable=default-role, --max-line-length=75] - files: ^docs/ + args: [--enable=all, --disable=default-role, --max-line-length=75, -v] + files: ^docs\/|^.*\.(rst$|md$) - repo: https://github.com/pre-commit/mirrors-clang-format rev: v17.0.6 hooks: diff --git a/CHANGES.md b/CHANGES.md index 17aa4c6..45c7e26 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,12 @@ +v1.3.4 (21 February 2023) +======================== + +* Include .clang-format as a pre-commit hook, to ensure consistent code + style (improved readability, easier maintenance). +* Fixed `make test` to account for the changes in + [v1.3.0](https://github.com/PJ-Watson/sep-pjw/releases/tag/v1.3.0). +* All header files include the correct definitions. + v1.3.3 (7 February 2023) ======================== diff --git a/bench.py b/bench.py index caa2448..1bfb1e1 100755 --- a/bench.py +++ b/bench.py @@ -1,4 +1,7 @@ #!/usr/bin/env python + +"""Benchmarking SEP-PJW against equivalent photutils functions.""" + from __future__ import print_function import time diff --git a/ctest/compare.py b/ctest/compare.py index 770705e..dccda53 100755 --- a/ctest/compare.py +++ b/ctest/compare.py @@ -1,11 +1,26 @@ #!/usr/bin/env python +"""Compare the output from SEP-PJW against SExtractor.""" + from __future__ import print_function import sys def read_table(fname): + """ + Read in a whitespace-separated table. + + Parameters + ---------- + fname : path-like + The filename of the table to be opened. + + Returns + ------- + List[List[int|float]] + A list of rows, each of which is a list of the values in that row. + """ rows = [] for line in open(fname, "r"): l = line.strip() @@ -23,6 +38,19 @@ def read_table(fname): def fracdiff(x, y): + """ + Return the fraction difference between two numbers. + + Parameters + ---------- + x, y : float + The numbers to be compared. + + Returns + ------- + float + The fractional difference. + """ return abs((y - x) / max(y, x)) diff --git a/docs/conf.py b/docs/conf.py index df60a84..a33b44f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,6 +12,8 @@ # All configuration values have a default; values that are commented out # serve to show the default. +"""The sphinx configuration file for the SEP-PJW documentation.""" + import os import sys diff --git a/pyproject.toml b/pyproject.toml index 6bbf11c..3059da4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ authors = [ { name="Michael Wuertenberger"}, { name="Ingvar Stepanyan"}, { name="Gabe Brammer"}, - { name="Peter Watson", email="peter.watson+sep@inaf.it" }, + { name="Peter Watson"}, ] maintainers = [ { name="Peter Watson", email="peter.watson+sep@inaf.it" }, diff --git a/setup.py b/setup.py index 937edee..5394f14 100755 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +"""Setup file to build the C library.""" + import os import re import sys diff --git a/src/util.c b/src/util.c index acd9257..2729331 100644 --- a/src/util.c +++ b/src/util.c @@ -25,7 +25,7 @@ #define DETAILSIZE 512 -const char * const sep_version_string = "1.3.2"; +const char * const sep_version_string = "1.3.4"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ diff --git a/test.py b/test.py index a2fadde..231a6b5 100755 --- a/test.py +++ b/test.py @@ -1,4 +1,7 @@ #!/usr/bin/env py.test + +"""Test the python functionality of SEP-PJW.""" + from __future__ import division, print_function import os @@ -6,6 +9,7 @@ import numpy as np import pytest import sep_pjw as sep +from numpy.lib import recfunctions as rfn from numpy.testing import assert_allclose, assert_approx_equal, assert_equal # unicode_literals doesn't play well with numpy dtype field names @@ -63,9 +67,15 @@ def assert_allclose_structured(x, y): - """Assert that two structured arrays are close. + """ + Assert that two structured arrays are close. Compares floats relatively and everything else exactly. + + Parameters + ---------- + x, y : array-like + Structured arrays to be compared. """ assert x.dtype == y.dtype for name in x.dtype.names: @@ -76,13 +86,29 @@ def assert_allclose_structured(x, y): def matched_filter_snr(data, noise, kernel): - """Super slow implementation of matched filter SNR for testing. + r""" + Super slow implementation of matched filter SNR for testing. + + At each output pixel :math:`i`, the value is: - At each pixel, value is + .. math:: - sum(data[i] * kernel[i] / noise[i]^2) - ------------------------------------- - sqrt(sum(kernel[i]^2 / noise[i]^2)) + \frac{\sum(\text{data}[i] * \text{kernel}[i] / \text{noise}[i]^2)} + {\sqrt\sum(\text{kernel}[i]^2 / \text{noise}[i]^2)} + + Parameters + ---------- + data : array-like + The 2D data to be tested. + noise : array-like + The noise corresponding to the input ``data``. + kernel : array-like + The kernel used for filtering. + + Returns + ------- + array-like + The output SNR array, the same size as ``data``. """ ctr = kernel.shape[0] // 2, kernel.shape[1] // 2 kslice = ( @@ -138,7 +164,8 @@ def matched_filter_snr(data, noise, kernel): @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_vs_sextractor(): - """Test behavior of sep versus sextractor. + """ + Test behavior of sep versus sextractor. Note: we turn deblending off for this test. This is because the deblending algorithm uses a random number generator. Since the sequence @@ -282,6 +309,14 @@ def test_vs_sextractor(): def test_masked_background(): + """ + Check the background filtering. + + Check that the derived background is consistent with an explicit + mask, masking no pixels. Also check that the expected result is + returned if certain pixels are masked. + """ + data = 0.1 * np.ones((6, 6)) data[1, 1] = 1.0 data[4, 1] = 1.0 @@ -313,7 +348,9 @@ def test_masked_background(): @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_background_special(): - """Test special methods of Background""" + """ + Test the special methods of `sep_pjw.Background`. + """ bkg = sep.Background(image_data, bw=64, bh=64, fw=3, fh=3) @@ -329,7 +366,9 @@ def test_background_special(): def test_background_boxsize(): - """Test that background works when boxsize is same as image""" + """ + Test that `sep_pjw.Background` works when boxsize is same as image. + """ ny, nx = 100, 100 data = np.ones((ny, nx), dtype=np.float64) @@ -338,7 +377,9 @@ def test_background_boxsize(): def test_background_rms(): - """Test that Background.rms() at least works""" + """ + Test that `sep_pjw.Background.rms` at least works. + """ ny, nx = 1024, 1024 data = np.random.randn(ny, nx) @@ -354,6 +395,12 @@ def test_background_rms(): @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_extract_with_noise_array(): + """ + Test extraction with a flat noise array. + + This checks that a constant noise array gives the same result as + extracting without a noise array, for a given threshold. + """ # Get some background-subtracted test data: data = np.copy(image_data) @@ -391,7 +438,8 @@ def test_extract_with_noise_array(): def test_extract_with_noise_convolution(): - """Test extraction when there is both noise and convolution. + """ + Test extraction when there is both noise and convolution. This will use the matched filter implementation, and will handle bad pixels and edge effects gracefully. @@ -432,8 +480,12 @@ def test_extract_with_noise_convolution(): def test_extract_matched_filter_at_edge(): - """Exercise bug where bright star at end of image not detected - with noise array and matched filter on.""" + """ + Test bright source detection at the edge of an image. + + Exercise bug where bright star at end of image not detected + with noise array and matched filter on. + """ data = np.zeros((20, 20)) err = np.ones_like(data) @@ -455,6 +507,9 @@ def test_extract_matched_filter_at_edge(): @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_extract_with_mask(): + """ + Test that object detection only occurs in unmasked regions. + """ # Get some background-subtracted test data: data = np.copy(image_data) @@ -476,6 +531,12 @@ def test_extract_with_mask(): @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_extract_segmentation_map(): + """ + Test the returned segmentation map. + + Check that the segmentation map has the same dimensions as the input + image, and that the number of object pixels match the catalogue field. + """ # Get some background-subtracted test data: data = np.copy(image_data) @@ -492,6 +553,13 @@ def test_extract_segmentation_map(): @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_extract_seg_map_array(): + """ + Test the extraction when an existing segmentation map is supplied. + + Test that the returned catalogue is equal with and without a variable + noise array, and that the majority of fields match even when + deblending is performed on the original extraction. + """ # Get some background-subtracted test data: data = np.copy(image_data) @@ -553,6 +621,21 @@ def test_extract_seg_map_array(): else: assert_allclose(o_i[n], o_ii[n], atol=np.sqrt(o_i[v])) + # Perform a second test with deblending disabled. + objects3, segmap3 = sep.extract( + data, 1.5, err, segmentation_map=True, deblend_cont=1.0 + ) + + objects4, segmap4 = sep.extract( + data, 1.5, err, segmentation_map=segmap3, deblend_cont=1.0 + ) + + # The flag will not be the same, as the second extraction does not test + # for deblended objects. + objects3 = rfn.drop_fields(objects3, "flag") + objects4 = rfn.drop_fields(objects4, "flag") + assert_allclose_structured(objects3, objects4) + # ----------------------------------------------------------------------------- # aperture tests @@ -564,8 +647,12 @@ def test_extract_seg_map_array(): def test_aperture_dtypes(): - """Ensure that all supported image dtypes work in sum_circle() and - give the same answer""" + """ + Test the aperture extraction of multiple data types. + + Ensure that all supported image dtypes work in sum_circle() and + give the same answer. + """ r = 3.0 @@ -590,7 +677,9 @@ def test_apertures_small_ellipse_exact(): def test_apertures_all(): - """Test that aperture subpixel sampling works""" + """ + Test that aperture subpixel sampling works. + """ data = np.random.rand(*data_shape) r = 3.0 @@ -612,7 +701,9 @@ def test_apertures_all(): def test_apertures_exact(): - """Test area as measured by exact aperture modes on array of ones""" + """ + Test area as measured by exact aperture modes on array of ones. + """ theta = np.random.uniform(-np.pi / 2.0, np.pi / 2.0, naper) ratio = np.random.uniform(0.2, 1.0, naper) @@ -641,7 +732,9 @@ def test_apertures_exact(): def test_aperture_bkgann_overlapping(): - """Test bkgann functionality in circular & elliptical apertures.""" + """ + Test bkgann functionality in circular & elliptical apertures. + """ # If bkgann overlaps aperture exactly, result should be zero # (with subpix=1) @@ -657,7 +750,9 @@ def test_aperture_bkgann_overlapping(): def test_aperture_bkgann_ones(): - """Test bkgann functionality with flat data""" + """ + Test bkgann functionality with flat data. + """ data = np.ones(data_shape) r = 5.0 @@ -679,7 +774,9 @@ def test_aperture_bkgann_ones(): def test_masked_segmentation_measurements(): - """Test measurements with segmentation masking""" + """ + Test measurements with segmentation masking. + """ NX = 100 data = np.zeros((NX * 2, NX * 2)) @@ -756,6 +853,9 @@ def test_masked_segmentation_measurements(): def test_mask_ellipse(): + """ + Test that the correct number of elements are masked with an ellipse. + """ arr = np.zeros((20, 20), dtype=np.bool_) # should mask 5 pixels: @@ -768,6 +868,9 @@ def test_mask_ellipse(): def test_flux_radius(): + """ + Test that the correct radius is returned for varying flux fractions. + """ data = np.ones(data_shape) fluxfrac = [0.2**2, 0.3**2, 0.7**2, 1.0] true_r = [2.0, 3.0, 7.0, 10.0] @@ -779,7 +882,9 @@ def test_flux_radius(): def test_mask_ellipse_alt(): - """mask_ellipse with cxx, cyy, cxy parameters.""" + """ + Mask_ellipse with cxx, cyy, cxy parameters. + """ arr = np.zeros((20, 20), dtype=np.bool_) # should mask 5 pixels: @@ -796,9 +901,13 @@ def test_mask_ellipse_alt(): def test_byte_order_exception(): - """Test that error about byte order is raised with non-native + """ + Test that SEP-PJW will not run with non-native byte order. + + Test that error about byte order is raised with non-native byte order input array. This should happen for Background, extract, - and aperture functions.""" + and aperture functions. + """ data = np.ones((100, 100), dtype=np.float64) data = data.byteswap(True).newbyteorder() @@ -808,7 +917,9 @@ def test_byte_order_exception(): def test_set_pixstack(): - """Ensure that setting the pixel stack size works.""" + """ + Ensure that setting the pixel stack size works. + """ old = sep.get_extract_pixstack() new = old * 2 sep.set_extract_pixstack(new) @@ -817,7 +928,9 @@ def test_set_pixstack(): def test_set_sub_object_limit(): - """Ensure that setting the sub-object deblending limit works.""" + """ + Ensure that setting the sub-object deblending limit works. + """ old = sep.get_sub_object_limit() new = old * 2 sep.set_sub_object_limit(new) @@ -826,8 +939,12 @@ def test_set_sub_object_limit(): def test_long_error_msg(): - """Ensure that the error message is created successfully when - there is an error detail.""" + """ + Test the error handling in SEP-PJW. + + Ensure that the error message is created successfully when + there is an error detail. + """ # set extract pixstack to an insanely small value; this will trigger # a detailed error message when running sep.extract() From 8a050e3ab82cd67cf5b3e792ee519d112bcaa03b Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 12 Jun 2024 14:14:41 +0200 Subject: [PATCH 33/47] More support for NumPy 2.0 --- docs/tutorial.ipynb | 24 ++++++++++++++++++++---- pyproject.toml | 4 ++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/docs/tutorial.ipynb b/docs/tutorial.ipynb index 3ef4af8..1b2c1a9 100644 --- a/docs/tutorial.ipynb +++ b/docs/tutorial.ipynb @@ -351,15 +351,31 @@ "```\n", "ValueError: Input array with dtype '>f4' has non-native byte order.\n", "Only native byte order arrays are supported. To change the byte\n", - "order of the array 'data', do 'data = data.byteswap().newbyteorder()'\n", + "order of the array 'data', do 'data = data.astype(data.dtype.newbyteorder(\"=\"))'\n", "```\n", "\n", "It is usually easiest to do this byte-swap operation directly after\n", - "reading the array from the FITS file. You can even perform the byte\n", - "swap in-place by doing\n", + "reading the array from the FITS file. The exact procedure is slightly different,\n", + "depending on the version of ``numpy``. For ``numpy<2.0``, the operation was:\n", "\n", "```python\n", - ">>> data = data.byteswap(inplace=True).newbyteorder()\n", + "# Byte order changed in-place\n", + ">>> data = data.byteswap(inplace=True).newbyteorder() \n", + "# Data copied to a new array\n", + ">>> new_data = data.byteswap().newbyteorder()\n", + "```\n", + "\n", + "For ``numpy>=2.0``, the correct operation is one of the following:\n", + "\n", + "```python\n", + "# Copies data to a new array, preserves the ordering of the original array\n", + ">>> new_data = data.astype(data.dtype.newbyteorder(\"=\")) \n", + "# The same outcome as the previous operation\n", + ">>> new_data = data.byteswap()\n", + ">>> new_data = new_data.view(new_data.dtype.newbyteorder(\"=\"))\n", + "# Changes data in-place\n", + ">>> data = data.byteswap()\n", + ">>> data = data.view(data.dtype.newbyteorder(\"=\"))\n", "```\n", "\n", "If you do this in-place operation, ensure that there are no other\n", diff --git a/pyproject.toml b/pyproject.toml index 0ce7375..8262366 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ requires = [ "setuptools>=61.0", "wheel", - "numpy>=2.0.0.dev0", + "numpy>=2.0.0rc2", "Cython", "setuptools_scm>=8.0", ] @@ -40,7 +40,7 @@ classifiers=[ ] requires-python = ">=3.9" dependencies = [ - "numpy>=1.23" + "numpy>=1.23.5" ] [project.optional-dependencies] From 79cc2a4105eb52ef82be73d4af073f00df60439f Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:47:40 +0200 Subject: [PATCH 34/47] Numpy 2.0 compatibility. --- CHANGES.md | 13 +++++++++---- src/util.c | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 45c7e26..bf5e446 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,9 @@ -v1.3.4 (21 February 2023) +v1.3.5 (12 June 2024) +===================== + +* Small fixes and updates to ensure compatibility with NumPy 2.0. + +v1.3.4 (21 February 2024) ======================== * Include .clang-format as a pre-commit hook, to ensure consistent code @@ -7,7 +12,7 @@ v1.3.4 (21 February 2023) [v1.3.0](https://github.com/PJ-Watson/sep-pjw/releases/tag/v1.3.0). * All header files include the correct definitions. -v1.3.3 (7 February 2023) +v1.3.3 (7 February 2024) ======================== * Add changelog to documentation. @@ -16,7 +21,7 @@ v1.3.3 (7 February 2023) * Fix bug with precision loss when calculating threshold. * Improve error handling when object pixels exceed pix stack. -v1.3.2 (5 February 2023) +v1.3.2 (5 February 2024) ======================== * Move documentation to new location, fix package names and imports. @@ -24,7 +29,7 @@ v1.3.2 (5 February 2023) * Fix C compilation errors on windows (VLAs). * Publish updated version to PyPI under new name. -v1.3.1 (31 January 2023) +v1.3.1 (31 January 2024) ======================== * Formatting changes (follow [black](https://github.com/psf/black) diff --git a/src/util.c b/src/util.c index 2729331..3ae256c 100644 --- a/src/util.c +++ b/src/util.c @@ -25,7 +25,7 @@ #define DETAILSIZE 512 -const char * const sep_version_string = "1.3.4"; +const char * const sep_version_string = "1.3.5"; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ From bb3368efbf5806eda2e8a4c3df9ab857745493e8 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 7 Oct 2024 17:59:24 +0200 Subject: [PATCH 35/47] Fix flag return type. --- sep_pjw.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sep_pjw.pyx b/sep_pjw.pyx index e326828..561868e 100644 --- a/sep_pjw.pyx +++ b/sep_pjw.pyx @@ -586,7 +586,7 @@ cdef packed struct Object: np.int64_t ycpeak np.int64_t xpeak np.int64_t ypeak - long flag + short flag default_kernel = np.array([[1.0, 2.0, 1.0], [2.0, 4.0, 2.0], @@ -807,7 +807,7 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, ('ycpeak', np.int64), ('xpeak', np.int64), ('ypeak', np.int64), - ('flag', np.int_)])) + ('flag', np.short)])) for i in range(catalog.nobj): result['thresh'][i] = catalog.thresh[i] From b962af4f71e9a2de58aec923a251693ef11fed8d Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:04:42 +0200 Subject: [PATCH 36/47] Implement __array__ copy parameter. --- sep_pjw.pyx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sep_pjw.pyx b/sep_pjw.pyx index 561868e..9cf2c7b 100644 --- a/sep_pjw.pyx +++ b/sep_pjw.pyx @@ -443,7 +443,7 @@ cdef class Background: def __get__(self): return sep_bkg_globalrms(self.ptr) - def back(self, dtype=None): + def back(self, dtype=None, copy=None): """back(dtype=None) Create an array of the background. @@ -473,7 +473,10 @@ cdef class Background: status = sep_bkg_array(self.ptr, &buf[0, 0], sep_dtype) _assert_ok(status) - return result + if copy: + return result.copy() + else: + return result def rms(self, dtype=None): """rms(dtype=None) @@ -540,8 +543,8 @@ cdef class Background: status = sep_bkg_subarray(self.ptr, &buf[0, 0], sep_dtype) _assert_ok(status) - def __array__(self, dtype=None): - return self.back(dtype=dtype) + def __array__(self, dtype=None, copy=None): + return self.back(dtype=dtype, copy=copy) def __rsub__(self, np.ndarray data not None): data = np.copy(data) From 8355584f283fc85de0aeb870797161cd1243845d Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:16:37 +0200 Subject: [PATCH 37/47] Update tests and workflows. --- .github/workflows/build-wheels-upload-pypi.yml | 7 +++++++ .github/workflows/python-package-tox.yml | 11 ++++------- test.py | 18 +++++++++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-wheels-upload-pypi.yml b/.github/workflows/build-wheels-upload-pypi.yml index e8d39ec..5855c17 100644 --- a/.github/workflows/build-wheels-upload-pypi.yml +++ b/.github/workflows/build-wheels-upload-pypi.yml @@ -18,7 +18,13 @@ jobs: # The first and second job run in parallel. # The uploading jos needs to have the other two finished without error. + # From now on, we run the tests before continuing with these jobs. + + run_tests: + uses: ./.github/workflows/python-package-tox.yml + build_sdist: + needs: [run_tests] # First the source distribution is done on ubuntu. This is not related # to any operating system, so we could do it on the default os. @@ -60,6 +66,7 @@ jobs: path: dist/*.tar.gz build_wheels: + needs: [run_tests] # Second the wheels are build for different OS and python versions. This is # done with the help of the `cibuildwheel` package. diff --git a/.github/workflows/python-package-tox.yml b/.github/workflows/python-package-tox.yml index eb4b0f3..2507b1b 100644 --- a/.github/workflows/python-package-tox.yml +++ b/.github/workflows/python-package-tox.yml @@ -1,21 +1,18 @@ # This workflow will install tox and use it to run tests. # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: Python package +name: run_tests -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] +on: [push, workflow_call, workflow_dispatch, pull_request] jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: python: ["3.9", "3.10", "3.11", "3.12"] + os: [windows-latest, macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v4 diff --git a/test.py b/test.py index c1ba49a..aeeb6dc 100755 --- a/test.py +++ b/test.py @@ -413,9 +413,17 @@ def test_extract_with_noise_array(): # convolved. Near edges, the convolution doesn't adjust for pixels # off edge boundaries. As a result, the convolved noise map is not # all ones. - objects = sep.extract(data, 1.5 * bkg.globalrms, filter_kernel=None) + # Deblending is also turned off, as this appears to differ slightly + # across platforms - see `test_vs_sextractor()`. + objects = sep.extract( + data, 1.5 * bkg.globalrms, filter_kernel=None, deblend_cont=1.0 + ) objects2 = sep.extract( - data, 1.5 * bkg.globalrms, err=np.ones_like(data), filter_kernel=None + data, + 1.5 * bkg.globalrms, + err=np.ones_like(data), + filter_kernel=None, + deblend_cont=1.0, ) names_to_remove = ["errx2", "erry2", "errxy"] @@ -423,18 +431,18 @@ def test_extract_with_noise_array(): objects = objects[names_to_keep] objects2 = objects2[names_to_keep] - assert_equal(objects, objects2) + assert_allclose_structured(objects, objects2) # Less trivial test where thresh is realistic. Still a flat noise map. noise = bkg.globalrms * np.ones_like(data) - objects2 = sep.extract(data, 1.5, err=noise, filter_kernel=None) + objects2 = sep.extract(data, 1.5, err=noise, filter_kernel=None, deblend_cont=1.0) names_to_remove = ["errx2", "erry2", "errxy"] names_to_keep = [i for i in objects.dtype.names if i not in names_to_remove] objects = objects[names_to_keep] objects2 = objects2[names_to_keep] - assert_equal(objects, objects2) + assert_allclose_structured(objects, objects2) def test_extract_with_noise_convolution(): From b34e550ebc75dde760b49a93abb0b3a01edbd527 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 7 Oct 2024 19:03:29 +0200 Subject: [PATCH 38/47] Fix minor typos, update changelog. --- .github/workflows/python-package-tox.yml | 2 +- CHANGES.md | 8 ++++++++ README.md | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-package-tox.yml b/.github/workflows/python-package-tox.yml index 2507b1b..336b288 100644 --- a/.github/workflows/python-package-tox.yml +++ b/.github/workflows/python-package-tox.yml @@ -1,7 +1,7 @@ # This workflow will install tox and use it to run tests. # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions -name: run_tests +name: CI on: [push, workflow_call, workflow_dispatch, pull_request] diff --git a/CHANGES.md b/CHANGES.md index bf5e446..c4ef21c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +v1.3.6 (7 October 2024) +======================= + +* Fix wrong int type in Windows + ([#2](https://github.com/PJ-Watson/sep-pjw/issues/2), thanks to + @acenko for pointing this out). +* Update tests to run on multiple operating systems. + v1.3.5 (12 June 2024) ===================== diff --git a/README.md b/README.md index 0d3b18d..090ec8c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ SEP-PJW Python and C library for Source Extraction and Photometry, forked from [kbarbary/sep](https://github.com/kbarbary/sep) to provide additional features and bug fixes. -![Build Status](https://github.com/PJ-Watson/sep-pjw/workflows/Python%20package/badge.svg) +![Build Status](https://github.com/PJ-Watson/sep-pjw/workflows/CI/badge.svg) [![PyPI](https://img.shields.io/pypi/v/sep-pjw.svg)](https://pypi.python.org/pypi/sep-pjw) [![Documentation Status](https://readthedocs.org/projects/sep-pjw/badge/?version=latest)](https://sep-pjw.readthedocs.io/en/latest/?badge=latest) [![JOSS](http://joss.theoj.org/papers/10.21105/joss.00058/status.svg)](http://dx.doi.org/10.21105/joss.00058) From d016588b2f29cce96612d17fa96c158dfc8e99eb Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Wed, 6 Nov 2024 08:17:07 -0800 Subject: [PATCH 39/47] Add Python 3.13 tests. --- .github/workflows/build-wheels-upload-pypi.yml | 6 +++--- .github/workflows/python-package-tox.yml | 2 +- .readthedocs.yaml | 2 +- README.md | 4 +++- pyproject.toml | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-wheels-upload-pypi.yml b/.github/workflows/build-wheels-upload-pypi.yml index 5855c17..fe7e6df 100644 --- a/.github/workflows/build-wheels-upload-pypi.yml +++ b/.github/workflows/build-wheels-upload-pypi.yml @@ -72,7 +72,7 @@ jobs: # done with the help of the `cibuildwheel` package. # # The wheels are built for Windows, Linux and MacOS and the python versions - # 3.9 - 3.12. + # 3.9 - 3.13. # # The three operating system could be done in parallel. name: Build wheels on ${{ matrix.os }} @@ -116,7 +116,7 @@ jobs: if: matrix.cibw_archs == 'aarch64' run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-*" + CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_MANYLINUX_I686_IMAGE: manylinux2014 CIBW_BUILD_VERBOSITY: 1 @@ -127,7 +127,7 @@ jobs: if: matrix.cibw_archs != 'aarch64' run: python -m cibuildwheel --output-dir wheelhouse env: - CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-*" + CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 CIBW_MANYLINUX_I686_IMAGE: manylinux2014 CIBW_BUILD_VERBOSITY: 1 diff --git a/.github/workflows/python-package-tox.yml b/.github/workflows/python-package-tox.yml index 336b288..753250d 100644 --- a/.github/workflows/python-package-tox.yml +++ b/.github/workflows/python-package-tox.yml @@ -11,7 +11,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python: ["3.9", "3.10", "3.11", "3.12"] + python: ["3.9", "3.10", "3.11", "3.12", "3.13"] os: [windows-latest, macos-latest, ubuntu-latest] steps: diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 773d60b..c1fb2ee 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,7 +8,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.12" # You can also specify other tool versions: # nodejs: "20" # rust: "1.70" diff --git a/README.md b/README.md index 090ec8c..847d4b6 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,10 @@ SEP-PJW Python and C library for Source Extraction and Photometry, forked from [kbarbary/sep](https://github.com/kbarbary/sep) to provide additional features and bug fixes. +[![PyPI](https://img.shields.io/pypi/v/sep-pjw?label=PyPI)](https://pypi.python.org/pypi/sep-pjw) +[![PyPI - Downloads](https://img.shields.io/pypi/dm/sep-pjw?label=PyPI%20Downloads) +](https://pypi.python.org/pypi/sep-pjw) ![Build Status](https://github.com/PJ-Watson/sep-pjw/workflows/CI/badge.svg) -[![PyPI](https://img.shields.io/pypi/v/sep-pjw.svg)](https://pypi.python.org/pypi/sep-pjw) [![Documentation Status](https://readthedocs.org/projects/sep-pjw/badge/?version=latest)](https://sep-pjw.readthedocs.io/en/latest/?badge=latest) [![JOSS](http://joss.theoj.org/papers/10.21105/joss.00058/status.svg)](http://dx.doi.org/10.21105/joss.00058) diff --git a/pyproject.toml b/pyproject.toml index 8262366..e2a1d1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,7 @@ version_file = "src/_version.py" [tool.black] line-length = 88 -target-version = ['py311'] +target-version = ['py312'] extend-exclude = '(.*.txt|.*.md|.*.toml|.*.odg)' preview = true From e7ab7641b82e8a380cfd428e9b5df9b282b38b57 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:29:08 -0800 Subject: [PATCH 40/47] Add dynamic C version. Not modified in the Makefile since v1.0.3! --- Makefile | 35 ++++++++++++++++++++++++++++------- setup.py | 19 +++++++++++++++++++ src/util.c | 5 ++++- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 2ba9cf9..bc8bc4e 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,41 @@ OS ?= $(shell sh -c 'uname -s | tr "[A-Z]" "[a-z]"') -SOMAJOR = 1 -SOMINOR = 0 -SOBUGFIX = 3 +DESCRIBE := $(shell git describe --match "v*" --always --tags) +DESCRIBE_PARTS := $(subst -, ,$(DESCRIBE)) + +VERSION_TAG := $(word 1,$(DESCRIBE_PARTS)) +COMMITS_SINCE_TAG := $(word 2,$(DESCRIBE_PARTS)) +BUILD_ID := $(word 3,$(DESCRIBE_PARTS)) + +VERSION := $(subst v,,$(VERSION_TAG)) +VERSION_PARTS := $(subst ., ,$(VERSION)) + +MAJOR := $(word 1,$(VERSION_PARTS)) +MINOR := $(word 2,$(VERSION_PARTS)) +MICRO := $(word 3,$(VERSION_PARTS)) + +NEXT_MICRO := $(shell echo $$(($(MICRO)+1))) + +ifeq ($(strip $(COMMITS_SINCE_TAG)),) +CURRENT_VERSION_MICRO := $(MAJOR).$(MINOR).$(MICRO) +CURRENT_MICRO := $(MICRO) +else +CURRENT_VERSION_MICRO := $(MAJOR).$(MINOR).$(NEXT_MICRO) +CURRENT_MICRO := $(NEXT_MICRO)-dev$(COMMITS_SINCE_TAG)$(if $(BUILD_ID),-${BUILD_ID},) +endif ifeq ($(OS), darwin) SONAME = libsep.dylib -SONAME_MAJOR = libsep.$(SOMAJOR).dylib -SONAME_FULL = libsep.$(SOMAJOR).$(SOMINOR).$(SOBUGFIX).dylib +SONAME_MAJOR = libsep.$(MAJOR).dylib +SONAME_FULL = libsep.$(MAJOR).$(MINOR).$(CURRENT_MICRO).dylib SONAME_FLAG = -install_name LDPATHENV = DYLD_LIBRARY_PATH else ifeq ($(OS), linux) SONAME = libsep.so -SONAME_MAJOR = libsep.so.$(SOMAJOR) -SONAME_FULL = libsep.so.$(SOMAJOR).$(SOMINOR).$(SOBUGFIX) +SONAME_MAJOR = libsep.so.$(MAJOR) +SONAME_FULL = libsep.so.$(MAJOR).$(MINOR).$(CURRENT_MICRO) SONAME_FLAG = -soname LDPATHENV = LD_LIBRARY_PATH else @@ -36,6 +56,7 @@ LDFLAGS ?= CPPFLAGS += -Isrc CFLAGS += -Wall -Wextra -Wcast-qual -O3 -fvisibility=hidden # -Werror +CFLAGS += -DSEP_VERSION_STRING=\"$(MAJOR).$(MINOR).$(CURRENT_MICRO)\" CFLAGS_LIB = $(CFLAGS) -fPIC LDFLAGS_LIB = $(LDFLAGS) -shared -Wl,$(SONAME_FLAG),$(SONAME_MAJOR) diff --git a/setup.py b/setup.py index a44bc59..15c1ac8 100755 --- a/setup.py +++ b/setup.py @@ -6,6 +6,19 @@ from glob import glob from setuptools import Extension, setup +from setuptools_scm import ScmVersion, get_version +from setuptools_scm.version import guess_next_version + + +def _new_version_scheme(version: ScmVersion) -> str: + + return version.format_next_version(guess_next_version, "{guessed}-dev{distance}") + + +def _new_local_scheme(version: ScmVersion) -> str: + + return version.format_choice("", "-{node}") + # from setuptools import setup from setuptools.dist import Distribution @@ -41,6 +54,12 @@ ("_USE_MATH_DEFINES", "1"), ("NPY_NO_DEPRECATED_API", "NPY_2_0_API_VERSION"), ], + extra_compile_args=[f"""-DSEP_VERSION_STRING='{ + get_version( + version_scheme=_new_version_scheme, + local_scheme=_new_local_scheme, + ) + }'"""], ) ] extensions = cythonize( diff --git a/src/util.c b/src/util.c index 3ae256c..8ae8990 100644 --- a/src/util.c +++ b/src/util.c @@ -25,7 +25,10 @@ #define DETAILSIZE 512 -const char * const sep_version_string = "1.3.5"; +#ifndef SEP_VERSION_STRING +#define SEP_VERSION_STRING "1.3.6" +#endif +const char * const sep_version_string = SEP_VERSION_STRING; static _Thread_local char _errdetail_buffer[DETAILSIZE] = ""; /****************************************************************************/ From e9545105ec4ebae52b73faff76815800ad4c1453 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:48:05 -0800 Subject: [PATCH 41/47] Fix character/string typo. --- setup.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 15c1ac8..121b9b3 100755 --- a/setup.py +++ b/setup.py @@ -20,6 +20,11 @@ def _new_local_scheme(version: ScmVersion) -> str: return version.format_choice("", "-{node}") +c_version_string = get_version( + version_scheme=_new_version_scheme, + local_scheme=_new_local_scheme, +) + # from setuptools import setup from setuptools.dist import Distribution @@ -54,12 +59,7 @@ def _new_local_scheme(version: ScmVersion) -> str: ("_USE_MATH_DEFINES", "1"), ("NPY_NO_DEPRECATED_API", "NPY_2_0_API_VERSION"), ], - extra_compile_args=[f"""-DSEP_VERSION_STRING='{ - get_version( - version_scheme=_new_version_scheme, - local_scheme=_new_local_scheme, - ) - }'"""], + extra_compile_args=['-DSEP_VERSION_STRING="' + c_version_string + '"'], ) ] extensions = cythonize( From fe543a2ac528d310c2db436a2bf9327e6abfb705 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:37:11 -0800 Subject: [PATCH 42/47] Add C test to CI --- .github/workflows/python-package-tox.yml | 4 ++-- tox.ini | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package-tox.yml b/.github/workflows/python-package-tox.yml index 753250d..c928cd8 100644 --- a/.github/workflows/python-package-tox.yml +++ b/.github/workflows/python-package-tox.yml @@ -23,5 +23,5 @@ jobs: - name: Install Tox and any other packages run: python -m pip install tox - name: Run Tox - # Run tox using the version of Python in `PATH` - run: tox -e py + # Run tox using the version of Python in `PATH`, append platform + run: tox -e py-$(python -c "import sys; print(sys.platform)") diff --git a/tox.ini b/tox.ini index 3d5c16b..ac52b79 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,14 @@ [tox] -minversion = 3.3 -envlist = py3 +minversion = 4.0 +envlist = py3-linux isolated_build = true [testenv] passenv = HOME deps = pytest astropy -commands = pytest test.py +allowlist_externals = make +commands = + linux: make test + darwin: make test + pytest test.py From 051cbf68110be93d5a2f13ff3af31e3e3d4b1cee Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Fri, 8 Nov 2024 02:42:10 -0800 Subject: [PATCH 43/47] Tox tests. --- .github/workflows/python-package-tox.yml | 8 ++++++-- tox.ini | 12 +++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-package-tox.yml b/.github/workflows/python-package-tox.yml index c928cd8..af50fa0 100644 --- a/.github/workflows/python-package-tox.yml +++ b/.github/workflows/python-package-tox.yml @@ -21,7 +21,11 @@ jobs: with: python-version: ${{ matrix.python }} - name: Install Tox and any other packages - run: python -m pip install tox + run: | + python -m pip install --upgrade pip + python -m pip install tox tox-gh-actions - name: Run Tox # Run tox using the version of Python in `PATH`, append platform - run: tox -e py-$(python -c "import sys; print(sys.platform)") + run: tox + env: + PLATFORM: ${{ matrix.os }} diff --git a/tox.ini b/tox.ini index ac52b79..d7322f7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,20 @@ [tox] minversion = 4.0 -envlist = py3-linux +envlist = py-{linux,macos,windows} isolated_build = true +[gh-actions:env] +PLATFORM = + ubuntu-latest: linux + macos-latest: macos + windows-latest: windows + [testenv] passenv = HOME deps = pytest astropy allowlist_externals = make commands = - linux: make test - darwin: make test pytest test.py + linux: make test + macos: make test From b8f1ecaff900c37f008952c0a099a2b3d21d178b Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:58:50 +0100 Subject: [PATCH 44/47] Update changelogs, document C API changes. --- CHANGES.md | 187 ++------------------------- docs/changelog.rst | 7 - docs/changelogs/changelog.rst | 22 ++++ docs/changelogs/changes_to_c_api.rst | 78 +++++++++++ docs/changelogs/new_changes.rst | 7 + docs/changelogs/original_changes.md | 179 +++++++++++++++++++++++++ docs/index.rst | 2 +- 7 files changed, 299 insertions(+), 183 deletions(-) delete mode 100644 docs/changelog.rst create mode 100644 docs/changelogs/changelog.rst create mode 100644 docs/changelogs/changes_to_c_api.rst create mode 100644 docs/changelogs/new_changes.rst create mode 100644 docs/changelogs/original_changes.md diff --git a/CHANGES.md b/CHANGES.md index c4ef21c..c831577 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,15 @@ +v1.3.7 (8 November 2024) +======================== + +* Test against Python 3.13. +* Update the Makefile to support Semantic Versioning from git tags. +* Update the C libraries to allow for passing the version as a compiler + flag. +* Update `setup.py` to pass the SCM version to the Cython compiler. +* Include C tests in the Tox suite (for linux and macos only). +* Document any and all changes to the C API since forking. +* Restructure changelog documentation. + v1.3.6 (7 October 2024) ======================= @@ -65,178 +77,3 @@ v1.3.0 (1 December 2023) ([#122](https://github.com/kbarbary/sep/issues/122 "Original issue"), inspired by [Gabe Brammer's fork](https://github.com/gbrammer/sep) with additional fixes). - - -v1.2.1 (1 June 2022) -==================== - -* Same as v1.2.0 but with new wheels for Python 3.10 and AArch64. - -v1.2.0 (1 May 2021) -=================== - -* Changed `numpy.float` and `numpy.int` types for deprecations in numpy 1.20 (#96). - -* Make it possible to safely invoke C library from multiple threads on - independent inputs. - - Global config functions such as `set_sub_object_limit()` - and `set_extract_pixstack()` still configure global params - (once for all threads), while other functions will retain their data - in thread-local storages, so they can be invoked from multiple threads as - long as they work on independent structures. - - Library compilation will now require a C11 compatible compiler, which should - be nowadays available on all supported platforms. - -* Mark some pointer parameters with `const *`. This is a backward-compatible - change, but makes it easier to extract constants that can be safely shared - between multiple threads and/or invocations. - -v1.1.1 (6 January 2021) -======================= - -* Same as v1.1.0 but with wheels built and uploaded to PyPI. Please report if you - have problems with wheels. - - -v1.1.0 (3 January 2021) -======================= - -* Add segmentation masking to the photometry and kron/auto functions (#69). - -* Add functions `sep.set_sub_object_limit(limit)` and `sep.get_sub_object_limit()` - for modifying and retrieving the sub-object deblending limit. Previously this - parameter was hard-coded to 1024. 1024 is now the default value. - -* This and future versions are now Python 3 only. Python 2 is no longer - supported. - -* Modernize setup.py with pyproject.toml - - -v1.0.3 (12 June 2018) -===================== - -* Fix double-free bug in sep_extract() arising when an error status occurs - and convolution is on. (#56) - -* Work around numpy dependency in setup. (#59) - - -v1.0.2 (19 September 2017) -========================== - -* Fix makefile so that `make install` works on OS X for the C library. - Python module and C code are unchanged. - - -v1.0.1 (10 July 2017) -===================== - -* Fix bug when using masked filter and noise array where objects with member - pixels at end of image (maximum y coordinate) were erroneously missed. - - -v1.0.0 (30 September 2016) -========================== - -* Remove features deprecated in previous versions. - -* Fix bug in Background.rms() giving nonsensical results. - -v0.6.0 (25 August 2016) -======================= - -* New, more coherent C API. This change should be transparent to users - of the Python module. - -* Add variance uncertainty parameters `errx2`, `erry2` and `errxy` to - output of `sep.extract()`. - -* Add a minimum sigma to `sep.winpos()` to match Source Extractor - behavior. - -* Fix use of boolean masks in `sep.kron_radius()`. Formerly, using a - boolean mask resulted in nonsense results. - -* Fix segfault in `Background.back()` when box size is same as image size. - -* Fix bug in creating long error messages on Python 3. - -v0.5.2 (4 January 2016) -======================= - -Adds OS X and Windows support. - -v0.5.1 (30 November 2015) -========================= - -Bugfix release for problem in setup.py in packaged code. - -v0.5.0 (22 November 2015) -========================= - -* `sep.extract()` now uses a more correct matched filter algorithm in the - presence of a noise array, rather than simple convolution. The `conv` - keyword has been changed to `filter_kernel` to reflect this, and a - `filter_type` keyword has been added to allow selecting the old behavior - of simple convolution. - -* `sep.extract()` now accepts a `mask` keyword argument. - -* `sep.extract()` can now return a segmentation map. - -* Special methods added to allow `data - bkg` and `np.array(bkg)` where - `bkg` is a Background object. - -v0.4.1 (10 November 2015) -========================= - -Bugfix release, fixing error estimate in `sep.sum_circle` and -`sep.sum_ellipse` when `bkgann` keyword argument is given. - -v0.4.0 (1 June 2015) -==================== - -* New `sep.winpos()` function. - -v0.3.0 (23 February 2015) -========================= - -* New `sep.flux_radius()` function. - -v0.2.0 (13 December 2014) -========================= - -* **[breaking change]** `theta` field in `extract()` output is now in - radians rather than degrees, for compatibility with new ellipse - aperture functions. - -* **[deprecation]** Change `mask_ellipse()` parameters from ellipse - coefficients to ellipse axes and position angle, to match aperture - functions. (Old behavior still works as well.) - -* **[deprecation]** Change `apercirc()` to `sum_circle()`, to match - new aperture functions. (Old name, `apercirc()`, still works.) - -* Add `sum_circann()`, `sum_ellipse()`, `sum_ellipann()`, - `kron_radius()`, `ellipse_coeffs()`, `ellipse_axes()` functions. - -* Exact mode aperture photometery in all functions, with `subpix=0`. - -* Enable variable thresholding in `sep.extract`. [#11] - -* Fix bug in background masking. This bug impacted masking in all - functions that used masking. Also affected C library. - -* More detail in error messages coming from within the C library. - More helpful error message for non-native byteorder arrays. - -* Add ability to change pixel stack size used in `extract()`, with - `set_extract_pixstack()` function - -v0.1.0 (11 August 2014) -======================= - -This is the first official release. diff --git a/docs/changelog.rst b/docs/changelog.rst deleted file mode 100644 index bdcdf9d..0000000 --- a/docs/changelog.rst +++ /dev/null @@ -1,7 +0,0 @@ -.. _changelog: - -Changelog -========= - -.. include:: ../CHANGES.md - :parser: myst_parser.sphinx_ diff --git a/docs/changelogs/changelog.rst b/docs/changelogs/changelog.rst new file mode 100644 index 0000000..53931e4 --- /dev/null +++ b/docs/changelogs/changelog.rst @@ -0,0 +1,22 @@ +.. _changelog: + +Changelog +========= + + +Upgrading From kbarbary/sep +--------------------------- + +.. toctree:: + :maxdepth: 2 + + changes_to_c_api + +Full Changelog +-------------- + +.. toctree:: + :maxdepth: 2 + + new_changes + original_changes diff --git a/docs/changelogs/changes_to_c_api.rst b/docs/changelogs/changes_to_c_api.rst new file mode 100644 index 0000000..b58bca5 --- /dev/null +++ b/docs/changelogs/changes_to_c_api.rst @@ -0,0 +1,78 @@ +Changes to the C API +==================== + +This page details cumulative changes to the C API between v1.2.1 and +v1.3.7, or since this package was forked from +`kbarbary/sep `_. Almost all changes to +the public API are to integer parameters, many of which have been changed +from ``int`` to ``int64_t``, to fix problems which may arise with +extremely large arrays. The other change of note is to the ``sep_image`` +struct, which enables the user to pass an existing segmentation map, and +re-extract all morphological and photometric quantities on a different +image. + +All changes here are transparent to users of the Python interface. + +.. c:struct:: sep_image + + - Three new parameters have been added: + + .. c:var:: int64_t * segids + + The unique ids in an existing segmentation map. + + .. c:var:: int64_t * idcounts + + The number of occurrences of each unique id in an existing segmentation map. + + .. c:var:: int64_t numids + + The total number of unique ids in an existing segmentation map. + + - The type of ``w``` and ``h``` has changed from ``int`` to ``int64_t``. + +.. c:struct:: sep_bkg + + - The type of the following parameters has changed from ``int`` to ``int64_t``: ``w``, ``h``, ``bw``, ``bh``, ``nx``, ``ny``, and ``n``. + +.. c:struct:: sep_catalog + + - The type of the following parameters has changed from ``int`` to ``int64_t``: ``npix``, ``tnpix``, ``xmin``, ``xmax``, ``ymin``, ``ymax``, ``xcpeak``, ``ycpeak``, ``xpeak``, ``ypeak``, ``pix``, and ``objectspix``. + +.. c:function:: int sep_background() + + - The type of the following parameters has changed from ``int`` to + ``int64_t``: ``bw``, ``bh``, ``fw``, and ``fh``. + +.. c:function:: float sep_bkg_pix() + + - The type of ``x`` and ``y`` has changed from ``int`` to ``int64_t``. + +.. c:function:: int sep_bkg_line() + + - The type of ``y`` has changed from ``int`` to ``int64_t``. + +.. c:function:: int sep_bkg_subline() + + - The type of ``y`` has changed from ``int`` to ``int64_t``. + +.. c:function:: int sep_bkg_rmsline() + + - The type of ``y`` has changed from ``int`` to ``int64_t``. + +.. c:function:: int sep_extract() + + - The type of ``convw`` and ``convh`` has changed from ``int`` to + ``int64_t``. + +.. c:function:: int sep_sum_circann_multi() + + - The type of ``n`` has changed from ``int`` to ``int64_t``. + +.. c:function:: int sep_flux_radius() + + - The type of ``n`` has changed from ``int`` to ``int64_t``. + +.. c:function:: void sep_set_ellipse() + + - The type of ``w`` and ``h`` has changed from ``int`` to ``int64_t``. diff --git a/docs/changelogs/new_changes.rst b/docs/changelogs/new_changes.rst new file mode 100644 index 0000000..fff7486 --- /dev/null +++ b/docs/changelogs/new_changes.rst @@ -0,0 +1,7 @@ +New Changelog +============= + +**Full Changelog** + +.. include:: ../../CHANGES.md + :parser: myst_parser.sphinx_ diff --git a/docs/changelogs/original_changes.md b/docs/changelogs/original_changes.md new file mode 100644 index 0000000..81d88cf --- /dev/null +++ b/docs/changelogs/original_changes.md @@ -0,0 +1,179 @@ +Original Changelog +================== + +This is the original changelog from [kbarbary/sep](https://github.com/kbarbary/sep), +copied here for posterity. + +v1.2.1 (1 June 2022) +-------------------- + +* Same as v1.2.0 but with new wheels for Python 3.10 and AArch64. + +v1.2.0 (1 May 2021) +------------------- + +* Changed `numpy.float` and `numpy.int` types for deprecations in numpy 1.20 (#96). + +* Make it possible to safely invoke C library from multiple threads on + independent inputs. + + Global config functions such as `set_sub_object_limit()` + and `set_extract_pixstack()` still configure global params + (once for all threads), while other functions will retain their data + in thread-local storages, so they can be invoked from multiple threads as + long as they work on independent structures. + + Library compilation will now require a C11 compatible compiler, which should + be nowadays available on all supported platforms. + +* Mark some pointer parameters with `const *`. This is a backward-compatible + change, but makes it easier to extract constants that can be safely shared + between multiple threads and/or invocations. + +v1.1.1 (6 January 2021) +----------------------- + +* Same as v1.1.0 but with wheels built and uploaded to PyPI. Please report if you + have problems with wheels. + + +v1.1.0 (3 January 2021) +----------------------- + +* Add segmentation masking to the photometry and kron/auto functions (#69). + +* Add functions `sep.set_sub_object_limit(limit)` and `sep.get_sub_object_limit()` + for modifying and retrieving the sub-object deblending limit. Previously this + parameter was hard-coded to 1024. 1024 is now the default value. + +* This and future versions are now Python 3 only. Python 2 is no longer + supported. + +* Modernize setup.py with pyproject.toml + + +v1.0.3 (12 June 2018) +--------------------- + +* Fix double-free bug in sep_extract() arising when an error status occurs + and convolution is on. (#56) + +* Work around numpy dependency in setup. (#59) + + +v1.0.2 (19 September 2017) +-------------------------- + +* Fix makefile so that `make install` works on OS X for the C library. + Python module and C code are unchanged. + + +v1.0.1 (10 July 2017) +--------------------- + +* Fix bug when using masked filter and noise array where objects with member + pixels at end of image (maximum y coordinate) were erroneously missed. + + +v1.0.0 (30 September 2016) +-------------------------- + +* Remove features deprecated in previous versions. + +* Fix bug in Background.rms() giving nonsensical results. + +v0.6.0 (25 August 2016) +----------------------- + +* New, more coherent C API. This change should be transparent to users + of the Python module. + +* Add variance uncertainty parameters `errx2`, `erry2` and `errxy` to + output of `sep.extract()`. + +* Add a minimum sigma to `sep.winpos()` to match Source Extractor + behavior. + +* Fix use of boolean masks in `sep.kron_radius()`. Formerly, using a + boolean mask resulted in nonsense results. + +* Fix segfault in `Background.back()` when box size is same as image size. + +* Fix bug in creating long error messages on Python 3. + +v0.5.2 (4 January 2016) +----------------------- + +Adds OS X and Windows support. + +v0.5.1 (30 November 2015) +------------------------- + +Bugfix release for problem in setup.py in packaged code. + +v0.5.0 (22 November 2015) +------------------------- + +* `sep.extract()` now uses a more correct matched filter algorithm in the + presence of a noise array, rather than simple convolution. The `conv` + keyword has been changed to `filter_kernel` to reflect this, and a + `filter_type` keyword has been added to allow selecting the old behavior + of simple convolution. + +* `sep.extract()` now accepts a `mask` keyword argument. + +* `sep.extract()` can now return a segmentation map. + +* Special methods added to allow `data - bkg` and `np.array(bkg)` where + `bkg` is a Background object. + +v0.4.1 (10 November 2015) +------------------------- + +Bugfix release, fixing error estimate in `sep.sum_circle` and +`sep.sum_ellipse` when `bkgann` keyword argument is given. + +v0.4.0 (1 June 2015) +-------------------- + +* New `sep.winpos()` function. + +v0.3.0 (23 February 2015) +------------------------- + +* New `sep.flux_radius()` function. + +v0.2.0 (13 December 2014) +------------------------- + +* **[breaking change]** `theta` field in `extract()` output is now in + radians rather than degrees, for compatibility with new ellipse + aperture functions. + +* **[deprecation]** Change `mask_ellipse()` parameters from ellipse + coefficients to ellipse axes and position angle, to match aperture + functions. (Old behavior still works as well.) + +* **[deprecation]** Change `apercirc()` to `sum_circle()`, to match + new aperture functions. (Old name, `apercirc()`, still works.) + +* Add `sum_circann()`, `sum_ellipse()`, `sum_ellipann()`, + `kron_radius()`, `ellipse_coeffs()`, `ellipse_axes()` functions. + +* Exact mode aperture photometery in all functions, with `subpix=0`. + +* Enable variable thresholding in `sep.extract`. [#11] + +* Fix bug in background masking. This bug impacted masking in all + functions that used masking. Also affected C library. + +* More detail in error messages coming from within the C library. + More helpful error message for non-native byteorder arrays. + +* Add ability to change pixel stack size used in `extract()`, with + `set_extract_pixstack()` function + +v0.1.0 (11 August 2014) +----------------------- + +This is the first official release. diff --git a/docs/index.rst b/docs/index.rst index 7f8131a..83b6ce1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -100,7 +100,7 @@ Usage Guide tutorial filter apertures - changelog + changelogs/changelog .. toctree:: :hidden: From 4be75c34082ae741b3613e00246defa31c98d54d Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Mon, 25 Nov 2024 19:12:52 +0100 Subject: [PATCH 45/47] Revert all references to sep-pjw, update docs and readme. --- .../workflows/build-wheels-upload-pypi.yml | 2 +- .gitignore | 1 - .pre-commit-config.yaml | 10 +-- MANIFEST.in | 4 +- README.md | 68 ++++++++++--------- bench.py | 17 +++-- ctest/compare.py | 2 +- docs/apertures.rst | 18 ++--- docs/changelogs/changes_to_c_api.rst | 15 ++-- docs/conf.py | 38 ++++++----- docs/filter.rst | 6 +- docs/index.rst | 46 +++++++------ docs/reference.rst | 54 +++++++-------- docs/rtd-pip-requirements | 1 + docs/tutorial.ipynb | 2 +- pyproject.toml | 8 +-- sep_pjw.pyx => sep.pyx | 2 +- setup.py | 4 +- src/background.c | 9 ++- test.py | 15 ++-- 20 files changed, 173 insertions(+), 149 deletions(-) rename sep_pjw.pyx => sep.pyx (99%) diff --git a/.github/workflows/build-wheels-upload-pypi.yml b/.github/workflows/build-wheels-upload-pypi.yml index fe7e6df..37aa5a5 100644 --- a/.github/workflows/build-wheels-upload-pypi.yml +++ b/.github/workflows/build-wheels-upload-pypi.yml @@ -156,7 +156,7 @@ jobs: needs: [build_wheels, build_sdist] environment: name: pypi - url: https://pypi.org/p/sep-pjw + url: https://pypi.org/p/sep permissions: id-token: write diff --git a/.gitignore b/.gitignore index ac8cb22..c4f278c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ MANIFEST # generated C file sep.c -sep_pjw.c # PyCharm .idea diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e616e43..a126746 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ exclude: "^data/" repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -13,22 +13,22 @@ repos: - id: isort name: isort (python) - repo: https://github.com/psf/black - rev: 23.12.1 + rev: 24.10.0 hooks: - id: black args: [--preview] - repo: https://github.com/numpy/numpydoc - rev: v1.6.0 + rev: v1.8.0 hooks: - id: numpydoc-validation - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v0.9.1 + rev: v1.0.0 hooks: - id: sphinx-lint args: [--enable=all, --disable=default-role, --max-line-length=75, -v] files: ^docs\/|^.*\.(rst$|md$) - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v17.0.6 + rev: v19.1.4 hooks: - id: clang-format types_or: [c++, c, cuda] diff --git a/MANIFEST.in b/MANIFEST.in index 52cf06e..178f13c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,5 +5,5 @@ include licenses/* include pyproject.toml include src/*.[c,h,i] -include sep_pjw.pyx -exclude sep_pjw.c +include sep.pyx +exclude sep.c diff --git a/README.md b/README.md index 847d4b6..f9259e4 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,36 @@ -SEP-PJW -======= +SEP +=== -Python and C library for Source Extraction and Photometry, forked from [kbarbary/sep](https://github.com/kbarbary/sep) to provide additional features and bug fixes. +Python and C library for Source Extraction and Photometry, originally +developed at [kbarbary/sep](https://github.com/kbarbary/sep) and later +maintained at [PJ-Watson/sep-pjw](https://github.com/PJ-Watson/sep-pjw). -[![PyPI](https://img.shields.io/pypi/v/sep-pjw?label=PyPI)](https://pypi.python.org/pypi/sep-pjw) -[![PyPI - Downloads](https://img.shields.io/pypi/dm/sep-pjw?label=PyPI%20Downloads) -](https://pypi.python.org/pypi/sep-pjw) -![Build Status](https://github.com/PJ-Watson/sep-pjw/workflows/CI/badge.svg) +[![PyPI](https://img.shields.io/pypi/v/sep?label=PyPI)](https://pypi.python.org/pypi/sep) +[![PyPI - Downloads](https://img.shields.io/pypi/dm/sep?label=PyPI%20Downloads) +](https://pypi.python.org/pypi/sep) +![Build Status](https://github.com/sep-developers/sep/workflows/CI/badge.svg) [![Documentation Status](https://readthedocs.org/projects/sep-pjw/badge/?version=latest)](https://sep-pjw.readthedocs.io/en/latest/?badge=latest) [![JOSS](http://joss.theoj.org/papers/10.21105/joss.00058/status.svg)](http://dx.doi.org/10.21105/joss.00058) *"... [it's] an SEP: Somebody Else's Problem." "Oh, good. I can relax then."* -SEP-PJW vs. SEP ---------------- - -The original release of `sep` by Kyle Barbary, -[kbarbary/sep](https://github.com/kbarbary/sep), no longer appears to be -maintained. Whilst the package is incredibly useful, there are a few -outstanding bugs, and support for the latest versions of Python -(`python>=3.11`) will be limited. The aim of `sep-pjw` is to offer a -version of `sep` that resolves these issues, whilst maintaining -compatibility as much as is feasibly possible. Any fixes or updates to -the original library are documented in [CHANGES.md](./CHANGES.md). For -existing workflows, the only necessary update will be to change the import -to -```python -import sep_pjw as sep -``` +SEP, SEP-PJW, and Package Names +------------------------------- + +`sep` was originally released by Kyle Barbary, at +[kbarbary/sep](https://github.com/kbarbary/sep) (``sep<=1.2.1``). For a +brief period, the package was maintained by Peter Watson, under the +`sep-pjw` package name, at +[PJ-Watson/sep-pjw](https://github.com/PJ-Watson/sep-pjw) and +[PyPI/sep-pjw](https://pypi.org/project/sep-pjw/) +(``1.3.0<=sep-pjw<=1.3.8``). Both of these repositories will be archived, +and future development will take place at +[sep-developers/sep]() +(``sep>=1.4.0``). Note that there may be some incompatibilities between +``sep==1.2.1`` and ``sep==1.4.0`` when using the C-API directly (to fix +an indexing bug arising with large arrays) - all changes are documented +[here](https://sep-pjw.readthedocs.io/en/latest/changelogs/changes_to_c_api.html). About @@ -72,19 +74,19 @@ Python **Install release version:** -SEP-PJW can be installed with [pip](https://pip.pypa.io): +SEP can be installed with [pip](https://pip.pypa.io): ``` -python -m pip install sep-pjw +python -m pip install sep ``` If you get an error about permissions, you are probably using your system Python. In this case, we recommend using [pip's "user install"](https://pip.pypa.io/en/latest/user_guide/#user-installs) -option to install sep-pjw into your user directory: +option to install sep into your user directory: ``` -python -m pip install --user sep-pjw +python -m pip install --user sep ``` Do **not** install sep or other third-party Python packages using @@ -153,14 +155,14 @@ and header file in `/path/to/prefix/include`. The default prefix is Contributing ------------ -- Report a bug or documentation issue: http://github.com/PJ-Watson/sep-pjw/issues -- Ask (or answer) a question: https://github.com/PJ-Watson/sep-pjw/discussions/categories/q-a +- Report a bug or documentation issue: http://github.com/sep-developers/issues +- Ask (or answer) a question: https://github.com/sep-developers/sep/discussions/categories/q-a -Development of SEP-PJW takes place on GitHub at -[PJ-Watson/sep-pjw](http://github.com/PJ-Watson/sep-pjw). Contributions of bug fixes, +Development of SEP takes place on GitHub at +[sep-developers/sep](http://github.com/sep-developers/sep). Contributions of bug fixes, documentation improvements and minor feature additions are welcome via GitHub pull requests. For major features, it is best to discuss the change first -via [GitHub Discussions](https://github.com/PJ-Watson/sep-pjw/discussions/). +via [GitHub Discussions](https://github.com/sep-developers/sep/discussions/). Citation @@ -280,4 +282,4 @@ the data. **I have more questions!** -Open a discussion on the [GitHub Discussions page](https://github.com/PJ-Watson/sep-pjw/discussions/categories/q-a)! +Open a discussion on the [GitHub Discussions page](https://github.com/sep-developers/sep/discussions/categories/q-a)! diff --git a/bench.py b/bench.py index 1bfb1e1..46be83a 100755 --- a/bench.py +++ b/bench.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -"""Benchmarking SEP-PJW against equivalent photutils functions.""" +"""Benchmarking SEP against equivalent photutils functions.""" from __future__ import print_function @@ -8,7 +8,8 @@ from os.path import join import numpy as np -import sep_pjw as sep + +import sep # try to import photutils for comparison timing try: @@ -75,18 +76,22 @@ if HAVE_PHOTUTILS: print("sep version: ", sep.__version__) print("photutils version:", photutils.__version__) - print(""" + print( + """ | test | sep | photutils | ratio | -|-------------------------|-----------------|-----------------|--------|""") +|-------------------------|-----------------|-----------------|--------|""" + ) blankline = ( "| | | | |" ) else: print("sep version: ", sep.__version__) - print(""" + print( + """ | test | sep | -|-------------------------|-----------------|""") +|-------------------------|-----------------|""" + ) blankline = "| | |" nloop = 50 diff --git a/ctest/compare.py b/ctest/compare.py index dccda53..d92b761 100755 --- a/ctest/compare.py +++ b/ctest/compare.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -"""Compare the output from SEP-PJW against SExtractor.""" +"""Compare the output from SEP against SExtractor.""" from __future__ import print_function diff --git a/docs/apertures.rst b/docs/apertures.rst index eef9258..32a1055 100644 --- a/docs/apertures.rst +++ b/docs/apertures.rst @@ -3,14 +3,14 @@ Aperture photometry There are four aperture functions available: -====================== ========================= +================== ========================= Function Sums data within... -====================== ========================= -`sep_pjw.sum_circle` circle(s) -`sep_pjw.sum_circann` circular annulus/annuli -`sep_pjw.sum_ellipse` ellipse(s) -`sep_pjw.sum_ellipann` elliptical annulus/annuli -====================== ========================= +================== ========================= +`sep.sum_circle` circle(s) +`sep.sum_circann` circular annulus/annuli +`sep.sum_ellipse` ellipse(s) +`sep.sum_ellipann` elliptical annulus/annuli +================== ========================= The follow examples demonstrate options for circular aperture photometry. The other functions behave similarly. @@ -89,13 +89,13 @@ Apply a mask (same shape as data). Pixels where the mask is True are **Local background subtraction** -The `~sep_pjw.sum_circle` and `~sep_pjw.sum_ellipse` functions have options +The `~sep.sum_circle` and `~sep.sum_ellipse` functions have options for performing local background subtraction. For example, to subtract the background calculated in an annulus between 6 and 8 pixel radius: .. code-block:: python - flux, fluxerr, flag = sep_pjw.sum_circle(data, objs['x'], objs['y'], 3.0, + flux, fluxerr, flag = sep.sum_circle(data, objs['x'], objs['y'], 3.0, mask=mask, bkgann=(6., 8.)) Pixels in the background annulus are not subsampled and any masked diff --git a/docs/changelogs/changes_to_c_api.rst b/docs/changelogs/changes_to_c_api.rst index b58bca5..8e18e72 100644 --- a/docs/changelogs/changes_to_c_api.rst +++ b/docs/changelogs/changes_to_c_api.rst @@ -2,7 +2,7 @@ Changes to the C API ==================== This page details cumulative changes to the C API between v1.2.1 and -v1.3.7, or since this package was forked from +v1.4.0, or since this package was forked from `kbarbary/sep `_. Almost all changes to the public API are to integer parameters, many of which have been changed from ``int`` to ``int64_t``, to fix problems which may arise with @@ -23,21 +23,26 @@ All changes here are transparent to users of the Python interface. .. c:var:: int64_t * idcounts - The number of occurrences of each unique id in an existing segmentation map. + The number of occurrences of each unique id in an existing + segmentation map. .. c:var:: int64_t numids The total number of unique ids in an existing segmentation map. - - The type of ``w``` and ``h``` has changed from ``int`` to ``int64_t``. + - The type of ``w`` and ``h`` has changed from ``int`` to ``int64_t``. .. c:struct:: sep_bkg - - The type of the following parameters has changed from ``int`` to ``int64_t``: ``w``, ``h``, ``bw``, ``bh``, ``nx``, ``ny``, and ``n``. + - The type of the following parameters has changed from ``int`` to + ``int64_t``: ``w``, ``h``, ``bw``, ``bh``, ``nx``, ``ny``, and ``n``. .. c:struct:: sep_catalog - - The type of the following parameters has changed from ``int`` to ``int64_t``: ``npix``, ``tnpix``, ``xmin``, ``xmax``, ``ymin``, ``ymax``, ``xcpeak``, ``ycpeak``, ``xpeak``, ``ypeak``, ``pix``, and ``objectspix``. + - The type of the following parameters has changed from ``int`` to + ``int64_t``: ``npix``, ``tnpix``, ``xmin``, ``xmax``, ``ymin``, + ``ymax``, ``xcpeak``, ``ycpeak``, ``xpeak``, ``ypeak``, ``pix``, and + ``objectspix``. .. c:function:: int sep_background() diff --git a/docs/conf.py b/docs/conf.py index a33b44f..5588fe8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,12 +12,20 @@ # All configuration values have a default; values that are commented out # serve to show the default. -"""The sphinx configuration file for the SEP-PJW documentation.""" +"""The sphinx configuration file for the SEP documentation.""" import os import sys -import sep_pjw +import sep + +try: + from sphinx_astropy.conf.v2 import * +except ImportError: + print( + "ERROR: the documentation requires the sphinx-astropy package to be installed." + ) + sys.exit(1) # generate api directory if it doesn't already exist if not os.path.exists("api"): @@ -34,10 +42,10 @@ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' -intersphinx_mapping = { - "python": ("http://docs.python.org/", None), - "numpy": ("http://docs.scipy.org/doc/numpy/", None), -} +# intersphinx_mapping = { +# "python": ("http://docs.python.org/", None), +# "numpy": ("http://docs.scipy.org/doc/numpy/", None), +# } # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -71,7 +79,7 @@ master_doc = "index" # General information about the project. -project = "sep-pjw" +project = "sep" copyright = "2014-2024, Peter Watson and contributors" # The version info for the project you're documenting, acts as replacement for @@ -79,10 +87,10 @@ # built documents. # # THe short X.Y version. -version = ".".join(sep_pjw.__version__.split(".")[0:2]) +version = ".".join(sep.__version__.split(".")[0:2]) # The full version, including alpha/beta/rc tags. -release = sep_pjw.__version__ +release = sep.__version__ html_title = f"{project} {release.split('+')[0]} documentation" @@ -129,9 +137,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -# html_theme = "sphinx_rtd_theme" -# html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] -html_theme = "furo" +html_theme = "pydata_sphinx_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -209,7 +215,7 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = "seppjwdoc" +htmlhelp_basename = "sepdoc" # -- Options for LaTeX output --------------------------------------------- @@ -227,7 +233,7 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ("index", "sep.tex", "sep Documentation", "Kyle Barbary", "manual"), + ("index", "sep.tex", "sep Documentation", "Peter Watson", "manual"), ] # The name of an image file (relative to this directory) to place at the top of @@ -255,7 +261,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [("index", "sep", "sep Documentation", ["Kyle Barbary"], 1)] +man_pages = [("index", "sep", "sep Documentation", ["Peter Watson"], 1)] # If true, show URL addresses after external links. # man_show_urls = False @@ -271,7 +277,7 @@ "index", "sep", "sep Documentation", - "Kyle Barbary", + "Peter Watson", "sep", "One line description of project.", "Miscellaneous", diff --git a/docs/filter.rst b/docs/filter.rst index 8eb473e..25d5b0c 100644 --- a/docs/filter.rst +++ b/docs/filter.rst @@ -4,11 +4,11 @@ Matched Filter (Convolution) For source detection, SEP supports using a matched filter, which can give the optimal detection signal-to-noise for objects with some known shape. This is controlled using the ``filter_kernel`` keyword in -`sep_pjw.extract`. For example: +`sep.extract`. For example: .. code-block:: python - import sep_pjw as sep + import sep kernel = np.array([[1., 2., 3., 2., 1.], [2., 3., 5., 3., 2.], @@ -35,7 +35,7 @@ Correct treatment in the presence of variable noise In Source Extractor, the matched filter is implemented assuming there is equal noise across all pixels in the kernel. The matched filter then simplifies to a convolution of the data with the kernel. In -`sep_pjw.extract`, this is also the behavior when there is constant noise +`sep.extract`, this is also the behavior when there is constant noise (when ``err`` is not specified). In the presence of independent noise on each pixel, SEP uses a full diff --git a/docs/index.rst b/docs/index.rst index 83b6ce1..2dd8cb3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ -SEP-PJW -======= +SEP +=== *Python library for Source Extraction and Photometry* @@ -49,16 +49,22 @@ instructions. - Masking of elliptical regions on images. -SEP-PJW vs. SEP -............... +SEP, SEP-PJW, and Package Names +............................... -The original release of ``sep`` by Kyle Barbary, -`kbarbary/sep `_, no longer appears to be -maintained. Whilst the package is incredibly useful, there are a few -outstanding bugs, and support for the latest versions of `Python` will be -limited. The aim of ``sep-pjw`` is to offer a version of sep that resolves -these issues, whilst maintaining compatibility as much as is feasibly -possible. +``sep`` was originally released by Kyle Barbary, at +`kbarbary/sep `_ (``sep<=1.2.1``). For a +brief period, the package was maintained by Peter Watson, under the +``sep-pjw`` package name, at +`PJ-Watson/sep-pjw `_ and +`PyPI/sep-pjw `_ +(``1.3.0<=sep-pjw<=1.3.8``). Both of these repositories will be archived, +and future development will take place at +`sep-developers/sep `_ +(``sep>=1.4.0``). +Note that there may be some incompatibilities between ``sep==1.2.1`` and +``sep==1.4.0`` when using the C-API directly -- the changes are documented +:doc:`here `. Installation @@ -67,27 +73,27 @@ Installation with conda .......... -SEP-PJW can be installed with conda from the ``conda-forge`` channel:: +SEP can be installed with conda from the ``conda-forge`` channel:: - conda install -c conda-forge sep-pjw + conda install -c conda-forge sep with pip ........ -SEP-PJW can also be installed with `pip `_. After +SEP can also be installed with `pip `_. After ensuring that numpy is installed, run :: - python -m pip install sep-pjw + python -m pip install sep If you get an error about permissions, you are probably using your system Python. In this case, I recommend using `pip's "user install" `_ option to install sep into your user directory :: - python -m pip install --user sep-pjw + python -m pip install --user sep -Do **not** install ``sep-pjw`` or other third-party Python packages using +Do **not** install ``sep`` or other third-party Python packages using ``sudo`` unless you are fully aware of the risks. @@ -114,10 +120,10 @@ Contributing ------------ Report a bug or documentation issue: -http://github.com/PJ-Watson/sep-pjw/issues +http://github.com/sep-developers/sep/issues -Development of ``sep-pjw`` takes place on GitHub at -http://github.com/PJ-Watson/sep-pjw. Contributions of bug fixes, +Development of ``sep`` takes place on GitHub at +http://github.com/sep-developers/sep. Contributions of bug fixes, documentation improvements and minor feature additions are welcome via GitHub pull requests. For major features, it is best to open an issue discussing the change first. diff --git a/docs/reference.rst b/docs/reference.rst index 833f8f7..07a114b 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -7,54 +7,54 @@ Reference/API .. autosummary:: :toctree: api - sep_pjw.Background - sep_pjw.extract + sep.Background + sep.extract **Aperture photometry** .. autosummary:: :toctree: api - sep_pjw.sum_circle - sep_pjw.sum_circann - sep_pjw.sum_ellipse - sep_pjw.sum_ellipann + sep.sum_circle + sep.sum_circann + sep.sum_ellipse + sep.sum_ellipann **Aperture utilities** .. autosummary:: :toctree: api - sep_pjw.kron_radius - sep_pjw.flux_radius - sep_pjw.winpos - sep_pjw.mask_ellipse - sep_pjw.ellipse_axes - sep_pjw.ellipse_coeffs + sep.kron_radius + sep.flux_radius + sep.winpos + sep.mask_ellipse + sep.ellipse_axes + sep.ellipse_coeffs **Low-level utilities** .. autosummary:: :toctree: api - sep_pjw.get_extract_pixstack - sep_pjw.set_extract_pixstack - sep_pjw.get_sub_object_limit - sep_pjw.set_sub_object_limit + sep.get_extract_pixstack + sep.set_extract_pixstack + sep.get_sub_object_limit + sep.set_sub_object_limit **Flags** -============================ =========================================== -Flag Description -============================ =========================================== -``sep_pjw.OBJ_MERGED`` object is result of deblending -``sep_pjw.OBJ_TRUNC`` object is truncated at image boundary -``sep_pjw.OBJ_SINGU`` x, y fully correlated in object -``sep_pjw.APER_TRUNC`` aperture truncated at image boundary -``sep_pjw.APER_HASMASKED`` aperture contains one or more masked pixels -``sep_pjw.APER_ALLMASKED`` aperture contains only masked pixels -``sep_pjw.APER_NONPOSITIVE`` aperture sum is negative in ``kron_radius`` -============================ =========================================== +======================== =========================================== +Flag Description +======================== =========================================== +``sep.OBJ_MERGED`` object is result of deblending +``sep.OBJ_TRUNC`` object is truncated at image boundary +``sep.OBJ_SINGU`` x, y fully correlated in object +``sep.APER_TRUNC`` aperture truncated at image boundary +``sep.APER_HASMASKED`` aperture contains one or more masked pixels +``sep.APER_ALLMASKED`` aperture contains only masked pixels +``sep.APER_NONPOSITIVE`` aperture sum is negative in ``kron_radius`` +======================== =========================================== To see if a given flag is set in ``flags``:: diff --git a/docs/rtd-pip-requirements b/docs/rtd-pip-requirements index 1f0edad..66ede4f 100644 --- a/docs/rtd-pip-requirements +++ b/docs/rtd-pip-requirements @@ -5,3 +5,4 @@ matplotlib fitsio numpydoc jupyter +sphinx-astropy[confv2] diff --git a/docs/tutorial.ipynb b/docs/tutorial.ipynb index 1b2c1a9..9f8f7a7 100644 --- a/docs/tutorial.ipynb +++ b/docs/tutorial.ipynb @@ -20,7 +20,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "import sep_pjw as sep" + "import sep" ] }, { diff --git a/pyproject.toml b/pyproject.toml index e2a1d1a..8bf48b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ requires = [ build-backend = "setuptools.build_meta" [project] -name = "sep-pjw" +name = "sep" dynamic = ["version"] authors = [ { name="Emmanuel Bertin"}, @@ -49,7 +49,7 @@ docs = [ "sphinx>=7.2", "numpydoc>=1.6", "matplotlib>=3.6", - "furo>=2024.1", + "sphinx-astropy[confv2]>=1.9.1", "nbsphinx>=0.9.3", "pandoc>=2.3", "fitsio>=1.2.1", @@ -58,8 +58,8 @@ docs = [ ] [project.urls] -"Homepage" = "https://github.com/PJ-Watson/sep-pjw" -"Bug Tracker" = "https://github.com/PJ-Watson/sep-pjw/issues" +"Homepage" = "https://github.com/sep-developers/sep" +"Bug Tracker" = "https://github.com/sep-developers/sep/issues" "Documentation" = "http://sep-pjw.readthedocs.org" [tool.setuptools_scm] diff --git a/sep_pjw.pyx b/sep.pyx similarity index 99% rename from sep_pjw.pyx rename to sep.pyx index 9cf2c7b..3be1a6d 100644 --- a/sep_pjw.pyx +++ b/sep.pyx @@ -731,7 +731,7 @@ def extract(np.ndarray data not None, float thresh, err=None, var=None, f"The number of object pixels ({np.nansum(idcounts)}) in " "the segmentation map exceeds the allocated pixel stack " f"({get_extract_pixstack()}). Use " - "`sep_pjw.set_extract_pixstack()` to increase the size, " + "`sep.set_extract_pixstack()` to increase the size, " "or check that the correct segmentation map has been " "supplied." ) diff --git a/setup.py b/setup.py index 121b9b3..2da55fa 100755 --- a/setup.py +++ b/setup.py @@ -46,12 +46,12 @@ def _new_local_scheme(version: ScmVersion) -> str: import numpy from Cython.Build import cythonize - sourcefiles = ["sep_pjw.pyx"] + glob(os.path.join("src", "*.c")) + sourcefiles = ["sep.pyx"] + glob(os.path.join("src", "*.c")) headerfiles = glob(os.path.join("src", "*.h")) include_dirs = [numpy.get_include(), "src"] extensions = [ Extension( - "sep_pjw", + "sep", sourcefiles, include_dirs=include_dirs, depends=headerfiles, diff --git a/src/background.c b/src/background.c index e726402..ddd1c60 100644 --- a/src/background.c +++ b/src/background.c @@ -505,11 +505,10 @@ float backguess(backstruct * bkg, float * mean, float * sigma) sig += dpix * i; } - med = hihigh >= histo - ? ((hihigh - histo) + 0.5 - + ((double)highsum - lowsum) - / (2.0 * (*hilow > *hihigh ? *hilow : *hihigh))) - : 0.0; + med = hihigh >= histo ? ((hihigh - histo) + 0.5 + + ((double)highsum - lowsum) + / (2.0 * (*hilow > *hihigh ? *hilow : *hihigh))) + : 0.0; if (sum) { mea /= (double)sum; sig = sig / sum - mea * mea; diff --git a/test.py b/test.py index aeeb6dc..b8f4829 100755 --- a/test.py +++ b/test.py @@ -1,6 +1,6 @@ #!/usr/bin/env py.test -"""Test the python functionality of SEP-PJW.""" +"""Test the python functionality of SEP.""" from __future__ import division, print_function @@ -8,10 +8,11 @@ import numpy as np import pytest -import sep_pjw as sep from numpy.lib import recfunctions as rfn from numpy.testing import assert_allclose, assert_approx_equal, assert_equal +import sep + # unicode_literals doesn't play well with numpy dtype field names @@ -349,7 +350,7 @@ def test_masked_background(): @pytest.mark.skipif(NO_FITS, reason="no FITS reader") def test_background_special(): """ - Test the special methods of `sep_pjw.Background`. + Test the special methods of `sep.Background`. """ bkg = sep.Background(image_data, bw=64, bh=64, fw=3, fh=3) @@ -367,7 +368,7 @@ def test_background_special(): def test_background_boxsize(): """ - Test that `sep_pjw.Background` works when boxsize is same as image. + Test that `sep.Background` works when boxsize is same as image. """ ny, nx = 100, 100 @@ -378,7 +379,7 @@ def test_background_boxsize(): def test_background_rms(): """ - Test that `sep_pjw.Background.rms` at least works. + Test that `sep.Background.rms` at least works. """ ny, nx = 1024, 1024 @@ -910,7 +911,7 @@ def test_mask_ellipse_alt(): def test_byte_order_exception(): """ - Test that SEP-PJW will not run with non-native byte order. + Test that SEP will not run with non-native byte order. Test that error about byte order is raised with non-native byte order input array. This should happen for Background, extract, @@ -948,7 +949,7 @@ def test_set_sub_object_limit(): def test_long_error_msg(): """ - Test the error handling in SEP-PJW. + Test the error handling in SEP. Ensure that the error message is created successfully when there is an error detail. From 23af3c57a1642ecb5dd4e2528b552d86e39b72de Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:51:55 +0100 Subject: [PATCH 46/47] Resolve additional merge conflict. --- src/extract.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/extract.c b/src/extract.c index 0df2534..7bffe95 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1179,25 +1179,6 @@ PIXTYPE get_mean_thresh(infostruct * info, pliststruct * pixel) { return (PIXTYPE)(thresh_accum / pix_accum); } -/************************** get_mean_thresh **********************************/ -/* -Compute an average threshold from all pixels in the cluster -*/ -PIXTYPE get_mean_thresh(infostruct *info, pliststruct *pixel) -{ - pliststruct *pixt; - int pix_accum=0; - PIXTYPE thresh_accum=0; - - for (pixt=pixel+info->firstpix; pixt>=pixel; - pixt=pixel+PLIST(pixt,nextpix)) - { - thresh_accum += PLISTPIX(pixt,thresh); - pix_accum++; - } - - return thresh_accum / pix_accum; -} /*****************************************************************************/ /* sep_catalog manipulations */ From 57ef72b5f2f4f0a05f2ecb3e5585ff23300e9ea2 Mon Sep 17 00:00:00 2001 From: Peter Watson <59647390+PJ-Watson@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:46:07 +0100 Subject: [PATCH 47/47] Fix bitwise comparison, remove tab indents. --- src/aperture.c | 4 +- src/aperture.i | 293 ++++++++++++++++++++++++------------------------- 2 files changed, 148 insertions(+), 149 deletions(-) diff --git a/src/aperture.c b/src/aperture.c index 5fb6ad1..ef8b8f4 100644 --- a/src/aperture.c +++ b/src/aperture.c @@ -485,7 +485,7 @@ int sep_sum_circann_multi( */ if (im->segmap) { if (id > 0) { - if ((sconvert(segt) > 0.) & (sconvert(segt) != id)) { + if ((sconvert(segt) > 0.) && (sconvert(segt) != id)) { *flag |= SEP_APER_HASMASKED; ismasked = 1; } @@ -737,7 +737,7 @@ int sep_kron_radius( */ if (im->segmap) { if (id > 0) { - if ((sconvert(segt) > 0.) & (sconvert(segt) != id)) { + if ((sconvert(segt) > 0.) && (sconvert(segt) != id)) { ismasked = 1; } } else { diff --git a/src/aperture.i b/src/aperture.i index 53067e4..53bd089 100644 --- a/src/aperture.i +++ b/src/aperture.i @@ -1,18 +1,27 @@ /* - Adding (void *) pointers is a GNU C extension, not part of standard C. - When compiling on Windows with MS Visual C compiler need to cast the - (void *) to something the size of one byte. + Adding (void *) pointers is a GNU C extension, not part of standard C. + When compiling on Windows with MS Visual C compiler need to cast the + (void *) to something the size of one byte. */ #if defined(_MSC_VER) - #define MSVC_VOID_CAST (char *) +#define MSVC_VOID_CAST (char *) #else - #define MSVC_VOID_CAST +#define MSVC_VOID_CAST #endif -int APER_NAME(const sep_image *im, - double x, double y, APER_ARGS, int id, int subpix, short inflag, - double *sum, double *sumerr, double *area, short *flag) -{ +int APER_NAME( + const sep_image * im, + double x, + double y, + APER_ARGS, + int id, + int subpix, + short inflag, + double * sum, + double * sumerr, + double * area, + short * flag +) { PIXTYPE pix, varpix; double dx, dy, dx1, dy2, offset, scale, scale2, tmp; double tv, sigtv, totarea, maskarea, overlap, rpix2; @@ -25,8 +34,9 @@ int APER_NAME(const sep_image *im, /* input checks */ APER_CHECKS; - if (subpix < 0) + if (subpix < 0) { return ILLEGAL_SUBPIX; + } /* initializations */ size = esize = msize = ssize = 0; @@ -36,168 +46,157 @@ int APER_NAME(const sep_image *im, errort = im->noise; *flag = 0; varpix = 0.0; - scale = 1.0/subpix; - scale2 = scale*scale; - offset = 0.5*(scale-1.0); + scale = 1.0 / subpix; + scale2 = scale * scale; + offset = 0.5 * (scale - 1.0); errisarray = 0; errisstd = 0; APER_INIT; /* get data converter(s) for input array(s) */ - if ((status = get_converter(im->dtype, &convert, &size))) + if ((status = get_converter(im->dtype, &convert, &size))) { return status; - if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) + } + if (im->mask && (status = get_converter(im->mdtype, &mconvert, &msize))) { return status; + } - if (im->segmap && (status = get_converter(im->sdtype, &sconvert, &ssize))) + if (im->segmap && (status = get_converter(im->sdtype, &sconvert, &ssize))) { return status; + } /* get image noise */ - if (im->noise_type != SEP_NOISE_NONE) - { - errisstd = (im->noise_type == SEP_NOISE_STDDEV); - if (im->noise) - { - errisarray = 1; - if ((status = get_converter(im->ndtype, &econvert, &esize))) - return status; - } - else - { - varpix = (errisstd)? im->noiseval * im->noiseval: im->noiseval; - } + if (im->noise_type != SEP_NOISE_NONE) { + errisstd = (im->noise_type == SEP_NOISE_STDDEV); + if (im->noise) { + errisarray = 1; + if ((status = get_converter(im->ndtype, &econvert, &esize))) { + return status; + } + } else { + varpix = (errisstd) ? im->noiseval * im->noiseval : im->noiseval; } + } /* get extent of box */ APER_BOXEXTENT; /* loop over rows in the box */ - for (iy=ymin; iyh) * im->w + xmin; - datat = MSVC_VOID_CAST im->data + pos*size; - if (errisarray) - errort = MSVC_VOID_CAST im->noise + pos*esize; - if (im->mask) - maskt = MSVC_VOID_CAST im->mask + pos*msize; - if (im->segmap) - segt = MSVC_VOID_CAST im->segmap + pos*ssize; - - /* loop over pixels in this row */ - for (ix=xmin; ixmask && (mconvert(maskt) > im->maskthresh)) - { - ismasked = 1; - } - - /* Segmentation image: - - If `id` is negative, require segmented pixels within the - aperture. - - If `id` is positive, mask pixels with nonzero segment ids - not equal to `id`. - - */ - if (im->segmap) - { - if (id > 0) - { - if ((sconvert(segt) > 0.) & (sconvert(segt) != id)) - { - ismasked = 1; - } - } else { - if (sconvert(segt) != -1*id) - { - ismasked = 1; - } - } - } - - if (ismasked > 0) - { - *flag |= SEP_APER_HASMASKED; - maskarea += overlap; - } - else - { - tv += pix*overlap; - sigtv += varpix*overlap; - } - - totarea += overlap; - - } /* closes "if pixel might be within aperture" */ - - /* increment pointers by one element */ - datat += size; - if (errisarray) - errort += esize; - maskt += msize; - segt += ssize; - } + for (iy = ymin; iy < ymax; iy++) { + /* set pointers to the start of this row */ + pos = (iy % im->h) * im->w + xmin; + datat = MSVC_VOID_CAST im->data + pos * size; + if (errisarray) { + errort = MSVC_VOID_CAST im->noise + pos * esize; + } + if (im->mask) { + maskt = MSVC_VOID_CAST im->mask + pos * msize; + } + if (im->segmap) { + segt = MSVC_VOID_CAST im->segmap + pos * ssize; + } + + /* loop over pixels in this row */ + for (ix = xmin; ix < xmax; ix++) { + dx = ix - x; + dy = iy - y; + rpix2 = APER_RPIX2; + if (APER_COMPARE1) { + if (APER_COMPARE2) /* might be partially in aperture */ { + if (subpix == 0) { + overlap = APER_EXACT; + } else { + dx += offset; + dy += offset; + overlap = 0.0; + for (sy = subpix; sy--; dy += scale) { + dx1 = dx; + dy2 = dy * dy; + for (sx = subpix; sx--; dx1 += scale) { + rpix2 = APER_RPIX2_SUBPIX; + if (APER_COMPARE3) { + overlap += scale2; + } + } + } + } + } else { + /* definitely fully in aperture */ + overlap = 1.0; + } + + pix = convert(datat); + + if (errisarray) { + varpix = econvert(errort); + if (errisstd) { + varpix *= varpix; + } + } + + ismasked = 0; + if (im->mask && (mconvert(maskt) > im->maskthresh)) { + ismasked = 1; + } + + /* Segmentation image: + + If `id` is negative, require segmented pixels within the + aperture. + + If `id` is positive, mask pixels with nonzero segment ids + not equal to `id`. + + */ + if (im->segmap) { + if (id > 0) { + if ((sconvert(segt) > 0.) && (sconvert(segt) != id)) { + ismasked = 1; + } + } else { + if (sconvert(segt) != -1 * id) { + ismasked = 1; + } + } + } + + if (ismasked > 0) { + *flag |= SEP_APER_HASMASKED; + maskarea += overlap; + } else { + tv += pix * overlap; + sigtv += varpix * overlap; + } + + totarea += overlap; + + } /* closes "if pixel might be within aperture" */ + + /* increment pointers by one element */ + datat += size; + if (errisarray) { + errort += esize; + } + maskt += msize; + segt += ssize; } + } /* correct for masked values */ - if (im->mask) - { - if (inflag & SEP_MASK_IGNORE) - totarea -= maskarea; - else - { - tv *= (tmp = totarea/(totarea-maskarea)); - sigtv *= tmp; - } + if (im->mask) { + if (inflag & SEP_MASK_IGNORE) { + totarea -= maskarea; + } else { + tv *= (tmp = totarea / (totarea - maskarea)); + sigtv *= tmp; } + } /* add poisson noise, only if gain > 0 */ - if (im->gain > 0.0 && tv>0.0) + if (im->gain > 0.0 && tv > 0.0) { sigtv += tv / im->gain; + } *sum = tv; *sumerr = sqrt(sigtv);