File indexing completed on 2024-04-28 15:10:11
0001 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 0002 0003 This file is part of SEP 0004 0005 SPDX-FileCopyrightText: 1993-2011 Emmanuel Bertin -- IAP /CNRS/UPMC 0006 SPDX-FileCopyrightText: 2014 SEP developers 0007 0008 SPDX-License-Identifier: LGPL-3.0-or-later 0009 */ 0010 0011 /* Note: was scan.c in SExtractor. */ 0012 0013 #include <math.h> 0014 #include <stdio.h> 0015 #include <stdlib.h> 0016 #include <string.h> 0017 #include "sep.h" 0018 #include "sepcore.h" 0019 #include "extract.h" 0020 0021 #define DETECT_MAXAREA 0 /* replaces prefs.ext_maxarea */ 0022 #define WTHRESH_CONVFAC 1e-4 /* Factor to apply to weights when */ 0023 /* thresholding filtered weight-maps */ 0024 0025 /* globals */ 0026 int plistexist_cdvalue, plistexist_thresh, plistexist_var; 0027 int plistoff_value, plistoff_cdvalue, plistoff_thresh, plistoff_var; 0028 int plistsize; 0029 size_t extract_pixstack = 1000000; 0030 0031 /* get and set pixstack */ 0032 void sep_set_extract_pixstack(size_t val) 0033 { 0034 extract_pixstack = val; 0035 } 0036 0037 size_t sep_get_extract_pixstack() 0038 { 0039 return extract_pixstack; 0040 } 0041 0042 int sortit(infostruct *info, objliststruct *objlist, int minarea, 0043 objliststruct *finalobjlist, 0044 int deblend_nthresh, double deblend_mincont, double gain); 0045 void plistinit(int hasconv, int hasvar); 0046 void clean(objliststruct *objlist, double clean_param, int *survives); 0047 int convert_to_catalog(objliststruct *objlist, int *survives, 0048 sep_catalog *cat, int w, int include_pixels); 0049 0050 int arraybuffer_init(arraybuffer *buf, void *arr, int dtype, int w, int h, 0051 int bufw, int bufh); 0052 void arraybuffer_readline(arraybuffer *buf); 0053 void arraybuffer_free(arraybuffer *buf); 0054 0055 /********************* array buffer functions ********************************/ 0056 0057 /* initialize buffer */ 0058 /* bufw must be less than or equal to w */ 0059 int arraybuffer_init(arraybuffer *buf, void *arr, int dtype, int w, int h, 0060 int bufw, int bufh) 0061 { 0062 int status, yl; 0063 status = RETURN_OK; 0064 0065 /* data info */ 0066 buf->dptr = arr; 0067 buf->dw = w; 0068 buf->dh = h; 0069 0070 /* buffer array info */ 0071 buf->bptr = NULL; 0072 QMALLOC(buf->bptr, PIXTYPE, bufw*bufh, status); 0073 buf->bw = bufw; 0074 buf->bh = bufh; 0075 0076 /* pointers to within buffer */ 0077 buf->midline = buf->bptr + bufw*(bufh/2); /* ptr to middle buffer line */ 0078 buf->lastline = buf->bptr + bufw*(bufh-1); /* ptr to last buffer line */ 0079 0080 status = get_array_converter(dtype, &(buf->readline), &(buf->elsize)); 0081 if (status != RETURN_OK) 0082 goto exit; 0083 0084 /* initialize yoff */ 0085 buf->yoff = -bufh; 0086 0087 /* read in lines until the first data line is one line above midline */ 0088 for (yl=0; yl < bufh - bufh/2 - 1; yl++) 0089 arraybuffer_readline(buf); 0090 0091 return status; 0092 0093 exit: 0094 free(buf->bptr); 0095 buf->bptr = NULL; 0096 return status; 0097 } 0098 0099 /* read a line into the buffer at the top, shifting all lines down one */ 0100 void arraybuffer_readline(arraybuffer *buf) 0101 { 0102 PIXTYPE *line; 0103 int y; 0104 0105 /* shift all lines down one */ 0106 for (line = buf->bptr; line < buf->lastline; line += buf->bw) 0107 memcpy(line, line + buf->bw, sizeof(PIXTYPE) * buf->bw); 0108 0109 /* which image line now corresponds to the last line in buffer? */ 0110 buf->yoff++; 0111 y = buf->yoff + buf->bh - 1; 0112 0113 if (y < buf->dh) 0114 buf->readline(buf->dptr + buf->elsize * buf->dw * y, buf->dw, 0115 buf->lastline); 0116 0117 return; 0118 } 0119 0120 void arraybuffer_free(arraybuffer *buf) 0121 { 0122 free(buf->bptr); 0123 buf->bptr = NULL; 0124 } 0125 0126 /* apply_mask_line: Apply the mask to the image and noise buffers. 0127 * 0128 * If convolution is off, masked values should simply be not 0129 * detected. For this, would be sufficient to either set data to zero or 0130 * set noise (if present) to infinity. 0131 * 0132 * If convolution is on, strictly speaking, a masked (unknown) pixel 0133 * should "poison" the convolved value whenever it is present in the 0134 * convolution kernel (e.g., NaN behavior). However, practically we'd 0135 * rather use a "best guess" for the value. Without doing 0136 * interpolation from neighbors, 0 is the best guess (assuming image 0137 * is background subtracted). 0138 * 0139 * For the purpose of the full matched filter, we should set noise = infinity. 0140 * 0141 * So, this routine sets masked pixels to zero in the image buffer and 0142 * infinity in the noise buffer (if present). It affects the first 0143 */ 0144 void apply_mask_line(arraybuffer *mbuf, arraybuffer *imbuf, arraybuffer *nbuf) 0145 { 0146 int i; 0147 0148 for (i=0; i<mbuf->bw; i++) 0149 { 0150 if (mbuf->lastline[i] > 0.0) 0151 { 0152 imbuf->lastline[i] = 0.0; 0153 if (nbuf) 0154 nbuf->lastline[i] = BIG; 0155 } 0156 } 0157 } 0158 0159 /****************************** extract **************************************/ 0160 int sep_extract(sep_image *image, float thresh, int thresh_type, 0161 int minarea, float *conv, int convw, int convh, 0162 int filter_type, int deblend_nthresh, double deblend_cont, 0163 int clean_flag, double clean_param, 0164 sep_catalog **catalog) 0165 { 0166 arraybuffer dbuf, nbuf, mbuf; 0167 infostruct curpixinfo, initinfo, freeinfo; 0168 objliststruct objlist; 0169 char newmarker; 0170 size_t mem_pixstack; 0171 int nposize, oldnposize; 0172 int w, h; 0173 int co, i, luflag, pstop, xl, xl2, yl, cn; 0174 int stacksize, convn, status; 0175 int bufh; 0176 int isvarthresh, isvarnoise; 0177 short trunflag; 0178 PIXTYPE relthresh, cdnewsymbol, pixvar, pixsig; 0179 float sum; 0180 pixstatus cs, ps; 0181 0182 infostruct *info, *store; 0183 objliststruct *finalobjlist; 0184 pliststruct *pixel, *pixt; 0185 char *marker; 0186 PIXTYPE *scan, *cdscan, *wscan, *dummyscan; 0187 PIXTYPE *sigscan, *workscan; 0188 float *convnorm; 0189 int *start, *end, *survives; 0190 pixstatus *psstack; 0191 char errtext[512]; 0192 sep_catalog *cat; 0193 0194 status = RETURN_OK; 0195 pixel = NULL; 0196 convnorm = NULL; 0197 scan = wscan = cdscan = dummyscan = NULL; 0198 sigscan = workscan = NULL; 0199 info = NULL; 0200 store = NULL; 0201 marker = NULL; 0202 psstack = NULL; 0203 start = end = NULL; 0204 finalobjlist = NULL; 0205 survives = NULL; 0206 cat = NULL; 0207 convn = 0; 0208 sum = 0.0; 0209 w = image->w; 0210 h = image->h; 0211 isvarthresh = 0; 0212 relthresh = 0.0; 0213 pixvar = 0.0; 0214 pixsig = 0.0; 0215 isvarnoise = 0; 0216 0217 mem_pixstack = sep_get_extract_pixstack(); 0218 0219 /* seed the random number generator consistently on each call to get 0220 * consistent results. rand() is used in deblending. */ 0221 srand(1); 0222 0223 /* Noise characteristics of the image: None, scalar or variable? */ 0224 if (image->noise_type == SEP_NOISE_NONE) { } /* nothing to do */ 0225 else if (image->noise == NULL) { 0226 /* noise is constant; we can set pixel noise now. */ 0227 if (image->noise_type == SEP_NOISE_STDDEV) { 0228 pixsig = image->noiseval; 0229 pixvar = pixsig * pixsig; 0230 } 0231 else if (image->noise_type == SEP_NOISE_VAR) { 0232 pixvar = image->noiseval; 0233 pixsig = sqrt(pixvar); 0234 } 0235 else { 0236 return UNKNOWN_NOISE_TYPE; 0237 } 0238 } 0239 else { 0240 /* noise is variable; we deal with setting pixvar and pixsig at each 0241 * pixel. */ 0242 isvarnoise = 1; 0243 } 0244 0245 /* Deal with relative thresholding. (For an absolute threshold 0246 * nothing needs to be done, as `thresh` should already contain the constant 0247 * threshold, and `isvarthresh` is already 0.) */ 0248 if (thresh_type == SEP_THRESH_REL) { 0249 0250 /* The image must have noise information. */ 0251 if (image->noise_type == SEP_NOISE_NONE) return RELTHRESH_NO_NOISE; 0252 0253 isvarthresh = isvarnoise; /* threshold is variable if noise is */ 0254 if (isvarthresh) { 0255 relthresh = thresh; /* used below to set `thresh` for each pixel. */ 0256 } 0257 else { 0258 /* thresh is constant; convert relative threshold to absolute */ 0259 thresh *= pixsig; 0260 } 0261 } 0262 0263 /* this is input `thresh` regardless of thresh_type. */ 0264 objlist.thresh = thresh; 0265 0266 /*Allocate memory for buffers */ 0267 stacksize = w+1; 0268 QMALLOC(info, infostruct, stacksize, status); 0269 QCALLOC(store, infostruct, stacksize, status); 0270 QMALLOC(marker, char, stacksize, status); 0271 QMALLOC(dummyscan, PIXTYPE, stacksize, status); 0272 QMALLOC(psstack, pixstatus, stacksize, status); 0273 QCALLOC(start, int, stacksize, status); 0274 QMALLOC(end, int, stacksize, status); 0275 if ((status = lutzalloc(w, h)) != RETURN_OK) 0276 goto exit; 0277 if ((status = allocdeblend(deblend_nthresh)) != RETURN_OK) 0278 goto exit; 0279 0280 /* Initialize buffers for input array(s). 0281 * The buffer size depends on whether or not convolution is active. 0282 * If not convolving, the buffer size is just a single line. If convolving, 0283 * the buffer height equals the height of the convolution kernel. 0284 */ 0285 bufh = conv ? convh : 1; 0286 status = arraybuffer_init(&dbuf, image->data, image->dtype, w, h, stacksize, 0287 bufh); 0288 if (status != RETURN_OK) goto exit; 0289 if (isvarnoise) { 0290 status = arraybuffer_init(&nbuf, image->noise, image->ndtype, w, h, 0291 stacksize, bufh); 0292 if (status != RETURN_OK) goto exit; 0293 } 0294 if (image->mask) { 0295 status = arraybuffer_init(&mbuf, image->mask, image->mdtype, w, h, 0296 stacksize, bufh); 0297 if (status != RETURN_OK) goto exit; 0298 } 0299 0300 /* `scan` (or `wscan`) is always a pointer to the current line being 0301 * processed. It might be the only line in the buffer, or it might be the 0302 * middle line. */ 0303 scan = dbuf.midline; 0304 if (isvarnoise) 0305 wscan = nbuf.midline; 0306 0307 /* More initializations */ 0308 initinfo.pixnb = 0; 0309 initinfo.flag = 0; 0310 initinfo.firstpix = initinfo.lastpix = -1; 0311 0312 for (xl=0; xl<stacksize; xl++) 0313 { 0314 marker[xl] = 0; 0315 dummyscan[xl] = -BIG; 0316 } 0317 0318 co = pstop = 0; 0319 objlist.nobj = 1; 0320 curpixinfo.pixnb = 1; 0321 0322 /* Init finalobjlist */ 0323 QMALLOC(finalobjlist, objliststruct, 1, status); 0324 finalobjlist->obj = NULL; 0325 finalobjlist->plist = NULL; 0326 finalobjlist->nobj = finalobjlist->npix = 0; 0327 0328 0329 /* Allocate memory for the pixel list */ 0330 plistinit((conv != NULL), (image->noise_type != SEP_NOISE_NONE)); 0331 if (!(pixel = objlist.plist = malloc(nposize=mem_pixstack*plistsize))) 0332 { 0333 status = MEMORY_ALLOC_ERROR; 0334 goto exit; 0335 } 0336 0337 /*----- at the beginning, "free" object fills the whole pixel list */ 0338 freeinfo.firstpix = 0; 0339 freeinfo.lastpix = nposize-plistsize; 0340 pixt = pixel; 0341 for (i=plistsize; i<nposize; i += plistsize, pixt += plistsize) 0342 PLIST(pixt, nextpix) = i; 0343 PLIST(pixt, nextpix) = -1; 0344 0345 /* can only use a matched filter when convolving and when there is a noise 0346 * array */ 0347 if (!(conv && isvarnoise)) 0348 filter_type = SEP_FILTER_CONV; 0349 0350 if (conv) 0351 { 0352 /* allocate memory for convolved buffers */ 0353 QMALLOC(cdscan, PIXTYPE, stacksize, status); 0354 if (filter_type == SEP_FILTER_MATCHED) 0355 { 0356 QMALLOC(sigscan, PIXTYPE, stacksize, status); 0357 QMALLOC(workscan, PIXTYPE, stacksize, status); 0358 } 0359 0360 /* normalize the filter */ 0361 convn = convw * convh; 0362 QMALLOC(convnorm, PIXTYPE, convn, status); 0363 for (i=0; i<convn; i++) 0364 sum += fabs(conv[i]); 0365 for (i=0; i<convn; i++) 0366 convnorm[i] = conv[i] / sum; 0367 } 0368 0369 /*----- MAIN LOOP ------ */ 0370 for (yl=0; yl<=h; yl++) 0371 { 0372 0373 ps = COMPLETE; 0374 cs = NONOBJECT; 0375 0376 /* Need an empty line for Lutz' algorithm to end gracely */ 0377 if (yl==h) 0378 { 0379 if (conv) 0380 { 0381 free(cdscan); 0382 cdscan = NULL; 0383 if (filter_type == SEP_FILTER_MATCHED) 0384 { 0385 for (xl=0; xl<stacksize; xl++) 0386 { 0387 sigscan[xl] = -BIG; 0388 } 0389 } 0390 } 0391 cdscan = dummyscan; 0392 } 0393 0394 else 0395 { 0396 arraybuffer_readline(&dbuf); 0397 if (isvarnoise) 0398 arraybuffer_readline(&nbuf); 0399 if (image->mask) 0400 { 0401 arraybuffer_readline(&mbuf); 0402 apply_mask_line(&mbuf, &dbuf, (isvarnoise? &nbuf: NULL)); 0403 } 0404 0405 /* filter the lines */ 0406 if (conv) 0407 { 0408 status = convolve(&dbuf, yl, convnorm, convw, convh, cdscan); 0409 if (status != RETURN_OK) 0410 goto exit; 0411 0412 if (filter_type == SEP_FILTER_MATCHED) 0413 { 0414 status = matched_filter(&dbuf, &nbuf, yl, convnorm, convw, 0415 convh, workscan, sigscan, 0416 image->noise_type); 0417 0418 if (status != RETURN_OK) 0419 goto exit; 0420 } 0421 } 0422 else 0423 { 0424 cdscan = scan; 0425 } 0426 } 0427 0428 trunflag = (yl==0 || yl==h-1)? SEP_OBJ_TRUNC: 0; 0429 0430 for (xl=0; xl<=w; xl++) 0431 { 0432 if (xl == w) 0433 cdnewsymbol = -BIG; 0434 else 0435 cdnewsymbol = cdscan[xl]; 0436 0437 newmarker = marker[xl]; /* marker at this pixel */ 0438 marker[xl] = 0; 0439 0440 curpixinfo.flag = trunflag; 0441 0442 /* set pixel variance/noise based on noise array */ 0443 if (isvarthresh) { 0444 if (xl == w || yl == h) { 0445 pixsig = pixvar = 0.0; 0446 } 0447 else if (image->noise_type == SEP_NOISE_VAR) { 0448 pixvar = wscan[xl]; 0449 pixsig = sqrt(pixvar); 0450 } 0451 else if (image->noise_type == SEP_NOISE_STDDEV) { 0452 pixsig = wscan[xl]; 0453 pixvar = pixsig * pixsig; 0454 } 0455 else { 0456 status = UNKNOWN_NOISE_TYPE; 0457 goto exit; 0458 } 0459 0460 /* set `thresh` (This is needed later, even 0461 * if filter_type is SEP_FILTER_MATCHED */ 0462 thresh = relthresh * pixsig; 0463 } 0464 0465 /* luflag: is pixel above thresh (Y/N)? */ 0466 if (filter_type == SEP_FILTER_MATCHED) 0467 luflag = ((xl != w) && (sigscan[xl] > relthresh))? 1: 0; 0468 else 0469 luflag = cdnewsymbol > thresh? 1: 0; 0470 0471 if (luflag) 0472 { 0473 /* flag the current object if we're near the image bounds */ 0474 if (xl==0 || xl==w-1) 0475 curpixinfo.flag |= SEP_OBJ_TRUNC; 0476 0477 /* point pixt to first free pixel in pixel list */ 0478 /* and increment the "first free pixel" */ 0479 pixt = pixel + (cn=freeinfo.firstpix); 0480 freeinfo.firstpix = PLIST(pixt, nextpix); 0481 curpixinfo.lastpix = curpixinfo.firstpix = cn; 0482 0483 /* set values for the new pixel */ 0484 PLIST(pixt, nextpix) = -1; 0485 PLIST(pixt, x) = xl; 0486 PLIST(pixt, y) = yl; 0487 PLIST(pixt, value) = scan[xl]; 0488 if (PLISTEXIST(cdvalue)) 0489 PLISTPIX(pixt, cdvalue) = cdnewsymbol; 0490 if (PLISTEXIST(var)) 0491 PLISTPIX(pixt, var) = pixvar; 0492 if (PLISTEXIST(thresh)) 0493 PLISTPIX(pixt, thresh) = thresh; 0494 0495 /* Check if we have run out of free pixels in objlist.plist */ 0496 if (freeinfo.firstpix==freeinfo.lastpix) 0497 { 0498 status = PIXSTACK_FULL; 0499 sprintf(errtext, 0500 "The limit of %d active object pixels over the " 0501 "detection threshold was reached. Check that " 0502 "the image is background subtracted and the " 0503 "detection threshold is not too low. If you " 0504 "need to increase the limit, use " 0505 "set_extract_pixstack.", 0506 (int)mem_pixstack); 0507 put_errdetail(errtext); 0508 goto exit; 0509 0510 /* The code in the rest of this block increases the 0511 * stack size as needed. Currently, it is never 0512 * executed. This is because it isn't clear that 0513 * this is a good idea: most times when the stack 0514 * overflows it is due to user error: too-low 0515 * threshold or image not background subtracted. */ 0516 0517 /* increase the stack size */ 0518 oldnposize = nposize; 0519 mem_pixstack = (int)(mem_pixstack * 2); 0520 nposize = mem_pixstack * plistsize; 0521 pixel = (pliststruct *)realloc(pixel, nposize); 0522 objlist.plist = pixel; 0523 if (!pixel) 0524 { 0525 status = MEMORY_ALLOC_ERROR; 0526 goto exit; 0527 } 0528 0529 /* set next free pixel to the start of the new block 0530 * and link up all the pixels in the new block */ 0531 PLIST(pixel+freeinfo.firstpix, nextpix) = oldnposize; 0532 pixt = pixel + oldnposize; 0533 for (i=oldnposize + plistsize; i<nposize; 0534 i += plistsize, pixt += plistsize) 0535 PLIST(pixt, nextpix) = i; 0536 PLIST(pixt, nextpix) = -1; 0537 0538 /* last free pixel is now at the end of the new block */ 0539 freeinfo.lastpix = nposize - plistsize; 0540 } 0541 /*------------------------------------------------------------*/ 0542 0543 /* if the current status on this line is not already OBJECT... */ 0544 /* start segment */ 0545 if (cs != OBJECT) 0546 { 0547 cs = OBJECT; 0548 if (ps == OBJECT) 0549 { 0550 if (start[co] == UNKNOWN) 0551 { 0552 marker[xl] = 'S'; 0553 start[co] = xl; 0554 } 0555 else 0556 marker[xl] = 's'; 0557 } 0558 else 0559 { 0560 psstack[pstop++] = ps; 0561 marker[xl] = 'S'; 0562 start[++co] = xl; 0563 ps = COMPLETE; 0564 info[co] = initinfo; 0565 } 0566 } 0567 0568 } /* closes if pixel above threshold */ 0569 0570 /* process new marker ---------------------------------------------*/ 0571 /* newmarker is marker[ ] at this pixel position before we got to 0572 it. We'll only enter this if marker[ ] was set on a previous 0573 loop iteration. */ 0574 if (newmarker) 0575 { 0576 if (newmarker == 'S') 0577 { 0578 psstack[pstop++] = ps; 0579 if (cs == NONOBJECT) 0580 { 0581 psstack[pstop++] = COMPLETE; 0582 info[++co] = store[xl]; 0583 start[co] = UNKNOWN; 0584 } 0585 else 0586 update(&info[co], &store[xl], pixel); 0587 ps = OBJECT; 0588 } 0589 0590 else if (newmarker == 's') 0591 { 0592 if ((cs == OBJECT) && (ps == COMPLETE)) 0593 { 0594 pstop--; 0595 xl2 = start[co]; 0596 update (&info[co-1],&info[co], pixel); 0597 if (start[--co] == UNKNOWN) 0598 start[co] = xl2; 0599 else 0600 marker[xl2] = 's'; 0601 } 0602 ps = OBJECT; 0603 } 0604 0605 else if (newmarker == 'f') 0606 ps = INCOMPLETE; 0607 0608 else if (newmarker == 'F') 0609 { 0610 ps = psstack[--pstop]; 0611 if ((cs == NONOBJECT) && (ps == COMPLETE)) 0612 { 0613 if (start[co] == UNKNOWN) 0614 { 0615 if ((int)info[co].pixnb >= minarea) 0616 { 0617 /* update threshold before object is processed */ 0618 objlist.thresh = thresh; 0619 0620 status = sortit(&info[co], &objlist, minarea, 0621 finalobjlist, 0622 deblend_nthresh,deblend_cont, 0623 image->gain); 0624 if (status != RETURN_OK) 0625 goto exit; 0626 } 0627 0628 /* free the chain-list */ 0629 PLIST(pixel+info[co].lastpix, nextpix) = 0630 freeinfo.firstpix; 0631 freeinfo.firstpix = info[co].firstpix; 0632 } 0633 else 0634 { 0635 marker[end[co]] = 'F'; 0636 store[start[co]] = info[co]; 0637 } 0638 co--; 0639 ps = psstack[--pstop]; 0640 } 0641 } 0642 } 0643 /* end of if (newmarker) ------------------------------------------*/ 0644 0645 /* update the info or end segment */ 0646 if (luflag) 0647 { 0648 update(&info[co], &curpixinfo, pixel); 0649 } 0650 else if (cs == OBJECT) 0651 { 0652 cs = NONOBJECT; 0653 if (ps != COMPLETE) 0654 { 0655 marker[xl] = 'f'; 0656 end[co] = xl; 0657 } 0658 else 0659 { 0660 ps = psstack[--pstop]; 0661 marker[xl] = 'F'; 0662 store[start[co]] = info[co]; 0663 co--; 0664 } 0665 } 0666 0667 } /*------------ End of the loop over the x's -----------------------*/ 0668 0669 } /*---------------- End of the loop over the y's -----------------------*/ 0670 0671 /* convert `finalobjlist` to an array of `sepobj` structs */ 0672 /* if cleaning, see which objects "survive" cleaning. */ 0673 if (clean_flag) 0674 { 0675 /* Calculate mthresh for all objects in the list (needed for cleaning) */ 0676 for (i=0; i<finalobjlist->nobj; i++) 0677 { 0678 status = analysemthresh(i, finalobjlist, minarea, thresh); 0679 if (status != RETURN_OK) 0680 goto exit; 0681 } 0682 0683 QMALLOC(survives, int, finalobjlist->nobj, status); 0684 clean(finalobjlist, clean_param, survives); 0685 } 0686 0687 /* convert to output catalog */ 0688 QCALLOC(cat, sep_catalog, 1, status); 0689 status = convert_to_catalog(finalobjlist, survives, cat, w, 1); 0690 if (status != RETURN_OK) goto exit; 0691 0692 exit: 0693 free(finalobjlist->obj); 0694 free(finalobjlist->plist); 0695 free(finalobjlist); 0696 freedeblend(); 0697 free(pixel); 0698 lutzfree(); 0699 free(info); 0700 free(store); 0701 free(marker); 0702 free(dummyscan); 0703 free(psstack); 0704 free(start); 0705 free(end); 0706 free(survives); 0707 arraybuffer_free(&dbuf); 0708 if (image->noise) 0709 arraybuffer_free(&nbuf); 0710 if (image->mask) 0711 arraybuffer_free(&mbuf); 0712 if (conv) 0713 free(convnorm); 0714 if (filter_type == SEP_FILTER_MATCHED) 0715 { 0716 free(sigscan); 0717 free(workscan); 0718 } 0719 0720 if (status != RETURN_OK) 0721 { 0722 /* free cdscan if we didn't do it on the last `yl` line */ 0723 if ((cdscan != NULL) && (cdscan != dummyscan)) 0724 free(cdscan); 0725 /* clean up catalog if it was allocated */ 0726 sep_catalog_free(cat); 0727 cat = NULL; 0728 } 0729 0730 *catalog = cat; 0731 return status; 0732 } 0733 0734 0735 /********************************* sortit ************************************/ 0736 /* 0737 build the object structure. 0738 */ 0739 int sortit(infostruct *info, objliststruct *objlist, int minarea, 0740 objliststruct *finalobjlist, 0741 int deblend_nthresh, double deblend_mincont, double gain) 0742 { 0743 objliststruct objlistout, *objlist2; 0744 static objstruct obj; 0745 int i, status; 0746 0747 status=RETURN_OK; 0748 objlistout.obj = NULL; 0749 objlistout.plist = NULL; 0750 objlistout.nobj = objlistout.npix = 0; 0751 0752 /*----- Allocate memory to store object data */ 0753 objlist->obj = &obj; 0754 objlist->nobj = 1; 0755 0756 memset(&obj, 0, (size_t)sizeof(objstruct)); 0757 objlist->npix = info->pixnb; 0758 obj.firstpix = info->firstpix; 0759 obj.lastpix = info->lastpix; 0760 obj.flag = info->flag; 0761 obj.thresh = objlist->thresh; 0762 0763 preanalyse(0, objlist); 0764 0765 status = deblend(objlist, 0, &objlistout, deblend_nthresh, deblend_mincont, 0766 minarea); 0767 if (status) 0768 { 0769 /* formerly, this wasn't a fatal error, so a flag was set for 0770 * the object and we continued. I'm leaving the flag-setting 0771 * here in case we want to change this to a non-fatal error in 0772 * the future, but currently the flag setting is irrelevant. */ 0773 objlist2 = objlist; 0774 for (i=0; i<objlist2->nobj; i++) 0775 objlist2->obj[i].flag |= SEP_OBJ_DOVERFLOW; 0776 goto exit; 0777 } 0778 else 0779 objlist2 = &objlistout; 0780 0781 /* Analyze the deblended objects and add to the final list */ 0782 for (i=0; i<objlist2->nobj; i++) 0783 { 0784 analyse(i, objlist2, 1, gain); 0785 0786 /* this does nothing if DETECT_MAXAREA is 0 (and it currently is) */ 0787 if (DETECT_MAXAREA && objlist2->obj[i].fdnpix > DETECT_MAXAREA) 0788 continue; 0789 0790 /* add the object to the final list */ 0791 status = addobjdeep(i, objlist2, finalobjlist); 0792 if (status != RETURN_OK) 0793 goto exit; 0794 } 0795 0796 exit: 0797 free(objlistout.plist); 0798 free(objlistout.obj); 0799 return status; 0800 } 0801 0802 0803 /********** addobjdeep (originally in manobjlist.c) **************************/ 0804 /* 0805 Add object number `objnb` from list `objl1` to list `objl2`. 0806 Unlike `addobjshallow` this also copies plist pixels to the second list. 0807 */ 0808 0809 int addobjdeep(int objnb, objliststruct *objl1, objliststruct *objl2) 0810 { 0811 objstruct *objl2obj; 0812 pliststruct *plist1 = objl1->plist, *plist2 = objl2->plist; 0813 int fp, i, j, npx, objnb2; 0814 0815 fp = objl2->npix; /* 2nd list's plist size in pixels */ 0816 j = fp*plistsize; /* 2nd list's plist size in bytes */ 0817 objnb2 = objl2->nobj; /* # of objects currently in 2nd list*/ 0818 0819 /* Allocate space in `objl2` for the new object */ 0820 if (objnb2) 0821 objl2obj = (objstruct *)realloc(objl2->obj, 0822 (++objl2->nobj)*sizeof(objstruct)); 0823 else 0824 objl2obj = (objstruct *)malloc((++objl2->nobj)*sizeof(objstruct)); 0825 0826 if (!objl2obj) 0827 goto earlyexit; 0828 objl2->obj = objl2obj; 0829 0830 /* Allocate space for the new object's pixels in 2nd list's plist */ 0831 npx = objl1->obj[objnb].fdnpix; 0832 if (fp) 0833 plist2 = (pliststruct *)realloc(plist2, (objl2->npix+=npx)*plistsize); 0834 else 0835 plist2 = (pliststruct *)malloc((objl2->npix=npx)*plistsize); 0836 0837 if (!plist2) 0838 goto earlyexit; 0839 objl2->plist = plist2; 0840 0841 /* copy the plist */ 0842 plist2 += j; 0843 for(i=objl1->obj[objnb].firstpix; i!=-1; i=PLIST(plist1+i,nextpix)) 0844 { 0845 memcpy(plist2, plist1+i, (size_t)plistsize); 0846 PLIST(plist2,nextpix) = (j+=plistsize); 0847 plist2 += plistsize; 0848 } 0849 PLIST(plist2-=plistsize, nextpix) = -1; 0850 0851 /* copy the object itself */ 0852 objl2->obj[objnb2] = objl1->obj[objnb]; 0853 objl2->obj[objnb2].firstpix = fp*plistsize; 0854 objl2->obj[objnb2].lastpix = j-plistsize; 0855 0856 return RETURN_OK; 0857 0858 /* if early exit, reset 2nd list */ 0859 earlyexit: 0860 objl2->nobj--; 0861 objl2->npix = fp; 0862 return MEMORY_ALLOC_ERROR; 0863 } 0864 0865 0866 0867 /****************************** plistinit ************************************ 0868 * (originally init_plist() in sextractor) 0869 PURPOSE initialize a pixel-list and its components. 0870 ***/ 0871 void plistinit(int hasconv, int hasvar) 0872 { 0873 pbliststruct *pbdum = NULL; 0874 0875 plistsize = sizeof(pbliststruct); 0876 plistoff_value = (char *)&pbdum->value - (char *)pbdum; 0877 0878 if (hasconv) 0879 { 0880 plistexist_cdvalue = 1; 0881 plistoff_cdvalue = plistsize; 0882 plistsize += sizeof(PIXTYPE); 0883 } 0884 else 0885 { 0886 plistexist_cdvalue = 0; 0887 plistoff_cdvalue = plistoff_value; 0888 } 0889 0890 if (hasvar) 0891 { 0892 plistexist_var = 1; 0893 plistoff_var = plistsize; 0894 plistsize += sizeof(PIXTYPE); 0895 0896 plistexist_thresh = 1; 0897 plistoff_thresh = plistsize; 0898 plistsize += sizeof(PIXTYPE); 0899 } 0900 else 0901 { 0902 plistexist_var = 0; 0903 plistexist_thresh = 0; 0904 } 0905 0906 return; 0907 0908 } 0909 0910 0911 /************************** clean an objliststruct ***************************/ 0912 /* 0913 Fill a list with whether each object in the list survived the cleaning 0914 (assumes that mthresh has already been calculated for all objects in the list) 0915 */ 0916 0917 void clean(objliststruct *objlist, double clean_param, int *survives) 0918 { 0919 objstruct *obj1, *obj2; 0920 int i,j; 0921 double amp,ampin,alpha,alphain, unitarea,unitareain,beta,val; 0922 float dx,dy,rlim; 0923 0924 beta = clean_param; 0925 0926 /* initialize to all surviving */ 0927 for (i=0; i<objlist->nobj; i++) 0928 survives[i] = 1; 0929 0930 obj1 = objlist->obj; 0931 for (i=0; i<objlist->nobj; i++, obj1++) 0932 { 0933 if (!survives[i]) 0934 continue; 0935 0936 /* parameters for test object */ 0937 unitareain = PI*obj1->a*obj1->b; 0938 ampin = obj1->fdflux/(2*unitareain*obj1->abcor); 0939 alphain = (pow(ampin/obj1->thresh, 1.0/beta)-1)*unitareain/obj1->fdnpix; 0940 0941 /* loop over remaining objects in list*/ 0942 obj2 = obj1 + 1; 0943 for (j=i+1; j<objlist->nobj; j++, obj2++) 0944 { 0945 if (!survives[j]) 0946 continue; 0947 0948 dx = obj1->mx - obj2->mx; 0949 dy = obj1->my - obj2->my; 0950 rlim = obj1->a + obj2->a; 0951 rlim *= rlim; 0952 if (dx*dx + dy*dy > rlim*CLEAN_ZONE*CLEAN_ZONE) 0953 continue; 0954 0955 /* if obj1 is bigger, see if it eats obj2 */ 0956 if (obj2->fdflux < obj1->fdflux) 0957 { 0958 val = 1 + alphain*(obj1->cxx*dx*dx + obj1->cyy*dy*dy + 0959 obj1->cxy*dx*dy); 0960 if (val>1.0 && ((float)(val<1e10?ampin*pow(val,-beta):0.0) > 0961 obj2->mthresh)) 0962 survives[j] = 0; /* the test object eats this one */ 0963 } 0964 0965 /* if obj2 is bigger, see if it eats obj1 */ 0966 else 0967 { 0968 unitarea = PI*obj2->a*obj2->b; 0969 amp = obj2->fdflux/(2*unitarea*obj2->abcor); 0970 alpha = (pow(amp/obj2->thresh, 1.0/beta) - 1) * 0971 unitarea/obj2->fdnpix; 0972 val = 1 + alpha*(obj2->cxx*dx*dx + obj2->cyy*dy*dy + 0973 obj2->cxy*dx*dy); 0974 if (val>1.0 && ((float)(val<1e10?amp*pow(val,-beta):0.0) > 0975 obj1->mthresh)) 0976 survives[i] = 0; /* this object eats the test object */ 0977 } 0978 0979 } /* inner loop over objlist (obj2) */ 0980 } /* outer loop of objlist (obj1) */ 0981 } 0982 0983 0984 /*****************************************************************************/ 0985 /* sep_catalog manipulations */ 0986 0987 void free_catalog_fields(sep_catalog *catalog) 0988 { 0989 free(catalog->thresh); 0990 free(catalog->npix); 0991 free(catalog->tnpix); 0992 free(catalog->xmin); 0993 free(catalog->xmax); 0994 free(catalog->ymin); 0995 free(catalog->ymax); 0996 free(catalog->x); 0997 free(catalog->y); 0998 free(catalog->x2); 0999 free(catalog->y2); 1000 free(catalog->xy); 1001 free(catalog->errx2); 1002 free(catalog->erry2); 1003 free(catalog->errxy); 1004 free(catalog->a); 1005 free(catalog->b); 1006 free(catalog->theta); 1007 free(catalog->cxx); 1008 free(catalog->cyy); 1009 free(catalog->cxy); 1010 free(catalog->cflux); 1011 free(catalog->flux); 1012 free(catalog->cpeak); 1013 free(catalog->peak); 1014 free(catalog->xcpeak); 1015 free(catalog->ycpeak); 1016 free(catalog->xpeak); 1017 free(catalog->ypeak); 1018 free(catalog->flag); 1019 1020 free(catalog->pix); 1021 free(catalog->objectspix); 1022 1023 memset(catalog, 0, sizeof(sep_catalog)); 1024 } 1025 1026 1027 /* convert_to_catalog() 1028 * 1029 * Convert the final object list to an output catalog. 1030 * 1031 * `survives`: array of 0 or 1 indicating whether or not to output each object 1032 * (ignored if NULL) 1033 * `cat`: catalog object to be filled. 1034 * `w`: width of image (used to calculate linear indicies). 1035 */ 1036 int convert_to_catalog(objliststruct *objlist, int *survives, 1037 sep_catalog *cat, int w, int include_pixels) 1038 { 1039 int i, j, k; 1040 int totnpix; 1041 int nobj = 0; 1042 int status = RETURN_OK; 1043 objstruct *obj; 1044 pliststruct *pixt, *pixel; 1045 1046 /* Set struct to zero in case the caller didn't. 1047 * This is important if there is a memory error and we have to call 1048 * free_catalog_fields() to recover at exit */ 1049 memset(cat, 0, sizeof(sep_catalog)); 1050 1051 /* Count number of surviving objects so that we can allocate the 1052 appropriate amount of space in the output catalog. */ 1053 if (survives) 1054 for (i=0; i<objlist->nobj; i++) nobj += survives[i]; 1055 else 1056 nobj = objlist->nobj; 1057 1058 /* allocate catalog fields */ 1059 cat->nobj = nobj; 1060 QMALLOC(cat->thresh, float, nobj, status); 1061 QMALLOC(cat->npix, int, nobj, status); 1062 QMALLOC(cat->tnpix, int, nobj, status); 1063 QMALLOC(cat->xmin, int, nobj, status); 1064 QMALLOC(cat->xmax, int, nobj, status); 1065 QMALLOC(cat->ymin, int, nobj, status); 1066 QMALLOC(cat->ymax, int, nobj, status); 1067 QMALLOC(cat->x, double, nobj, status); 1068 QMALLOC(cat->y, double, nobj, status); 1069 QMALLOC(cat->x2, double, nobj, status); 1070 QMALLOC(cat->y2, double, nobj, status); 1071 QMALLOC(cat->xy, double, nobj, status); 1072 QMALLOC(cat->errx2, double, nobj, status); 1073 QMALLOC(cat->erry2, double, nobj, status); 1074 QMALLOC(cat->errxy, double, nobj, status); 1075 QMALLOC(cat->a, float, nobj, status); 1076 QMALLOC(cat->b, float, nobj, status); 1077 QMALLOC(cat->theta, float, nobj, status); 1078 QMALLOC(cat->cxx, float, nobj, status); 1079 QMALLOC(cat->cyy, float, nobj, status); 1080 QMALLOC(cat->cxy, float, nobj, status); 1081 QMALLOC(cat->cflux, float, nobj, status); 1082 QMALLOC(cat->flux, float, nobj, status); 1083 QMALLOC(cat->cpeak, float, nobj, status); 1084 QMALLOC(cat->peak, float, nobj, status); 1085 QMALLOC(cat->xcpeak, int, nobj, status); 1086 QMALLOC(cat->ycpeak, int, nobj, status); 1087 QMALLOC(cat->xpeak, int, nobj, status); 1088 QMALLOC(cat->ypeak, int, nobj, status); 1089 QMALLOC(cat->cflux, float, nobj, status); 1090 QMALLOC(cat->flux, float, nobj, status); 1091 QMALLOC(cat->flag, short, nobj, status); 1092 1093 /* fill output arrays */ 1094 j = 0; /* running index in output array */ 1095 for (i=0; i<objlist->nobj; i++) 1096 { 1097 if ((survives == NULL) || survives[i]) 1098 { 1099 obj = objlist->obj + i; 1100 cat->thresh[j] = obj->thresh; 1101 cat->npix[j] = obj->fdnpix; 1102 cat->tnpix[j] = obj->dnpix; 1103 cat->xmin[j] = obj->xmin; 1104 cat->xmax[j] = obj->xmax; 1105 cat->ymin[j] = obj->ymin; 1106 cat->ymax[j] = obj->ymax; 1107 cat->x[j] = obj->mx; 1108 cat->y[j] = obj->my; 1109 cat->x2[j] = obj->mx2; 1110 cat->y2[j] = obj->my2; 1111 cat->xy[j] = obj->mxy; 1112 cat->errx2[j] = obj->errx2; 1113 cat->erry2[j] = obj->erry2; 1114 cat->errxy[j] = obj->errxy; 1115 1116 cat->a[j] = obj->a; 1117 cat->b[j] = obj->b; 1118 cat->theta[j] = obj->theta; 1119 1120 cat->cxx[j] = obj->cxx; 1121 cat->cyy[j] = obj->cyy; 1122 cat->cxy[j] = obj->cxy; 1123 1124 cat->cflux[j] = obj->fdflux; /* these change names */ 1125 cat->flux[j] = obj->dflux; 1126 cat->cpeak[j] = obj->fdpeak; 1127 cat->peak[j] = obj->dpeak; 1128 1129 cat->xpeak[j] = obj->xpeak; 1130 cat->ypeak[j] = obj->ypeak; 1131 cat->xcpeak[j] = obj->xcpeak; 1132 cat->ycpeak[j] = obj->ycpeak; 1133 1134 cat->flag[j] = obj->flag; 1135 1136 j++; 1137 } 1138 } 1139 1140 if (include_pixels) 1141 { 1142 /* count the total number of pixels */ 1143 totnpix = 0; 1144 for (i=0; i<cat->nobj; i++) totnpix += cat->npix[i]; 1145 1146 /* allocate buffer for all objects' pixels */ 1147 QMALLOC(cat->objectspix, int, totnpix, status); 1148 1149 /* allocate array of pointers into the above buffer */ 1150 QMALLOC(cat->pix, int*, nobj, status); 1151 1152 pixel = objlist->plist; 1153 1154 /* for each object, fill buffer and direct object's to it */ 1155 k = 0; /* current position in `objectspix` buffer */ 1156 j = 0; /* output object index */ 1157 for (i=0; i<objlist->nobj; i++) 1158 { 1159 obj = objlist->obj + i; /* input object */ 1160 if ((survives == NULL) || survives[i]) 1161 { 1162 /* point this object's pixel list into the buffer. */ 1163 cat->pix[j] = cat->objectspix + k; 1164 1165 /* fill the actual pixel values */ 1166 for (pixt=pixel+obj->firstpix; pixt>=pixel; 1167 pixt=pixel+PLIST(pixt,nextpix), k++) 1168 { 1169 cat->objectspix[k] = PLIST(pixt,x) + w*PLIST(pixt,y); 1170 } 1171 j++; 1172 } 1173 } 1174 } 1175 1176 exit: 1177 if (status != RETURN_OK) 1178 free_catalog_fields(cat); 1179 1180 return status; 1181 } 1182 1183 1184 void sep_catalog_free(sep_catalog *catalog) 1185 { 1186 if (catalog != NULL) 1187 free_catalog_fields(catalog); 1188 free(catalog); 1189 }