33 #include "muse_cplwrappers.h" 34 #include "muse_utils.h" 35 #include "muse_findstars.h" 40 #define DEBUG_MUSE_FINDSTARS 0 79 _cpl_matrix_sum_columns(
const cpl_matrix *
self)
82 cpl_matrix *vector = NULL;
86 cpl_size nr = cpl_matrix_get_nrow(
self);
87 cpl_size nc = cpl_matrix_get_ncol(
self);
89 vector = cpl_matrix_extract(
self, 0, 0, 1, 1, 1, nc);
91 const double *_self = cpl_matrix_get_data_const(
self);
93 double *_vector = cpl_matrix_get_data(vector);
98 for (ir = 1; ir < nr; ++ir) {
100 register cpl_size ic;
102 for (ic = 0; ic < nc; ++ic) {
103 _vector[ic] += _self[ir * nc + ic];
133 _cpl_matrix_sum_rows(
const cpl_matrix *
self)
136 cpl_matrix *vector = NULL;
140 cpl_size nr = cpl_matrix_get_nrow(
self);
141 cpl_size nc = cpl_matrix_get_ncol(
self);
143 vector = cpl_matrix_extract(
self, 0, 0, 1, 1, nr, 1);
145 const double *_self = cpl_matrix_get_data_const(
self);
147 double *_vector = cpl_matrix_get_data(vector);
149 register cpl_size ir;
152 for (ir = 0; ir < nr; ++ir) {
154 register cpl_size ic;
156 for (ic = 1; ic < nc; ++ic) {
157 _vector[ir] += _self[ir * nc + ic];
184 _cpl_matrix_sum(
const cpl_matrix *
self)
193 cpl_size sz = cpl_matrix_get_nrow(
self) * cpl_matrix_get_ncol(
self);
195 const double *_self = cpl_matrix_get_data_const(
self);
198 for (i = 0; i < sz; ++i) {
227 static cpl_error_code
228 _cpl_matrix_modulo(cpl_matrix *
self,
long value)
232 return CPL_ERROR_DIVISION_BY_ZERO;
235 cpl_size sz = cpl_matrix_get_nrow(
self) * cpl_matrix_get_ncol(
self);
237 double *m = cpl_matrix_get_data(
self);
241 *m = (long)(*m) % value;
245 return CPL_ERROR_NONE;
254 typedef int (*_cpl_matrix_element_compare_func)(
double aValue1,
double aValue2);
268 _muse_condition_not_equal(
double aValue1,
double aValue2)
270 return (aValue1 != aValue2) ? TRUE : FALSE;
284 _muse_condition_greater_equal(
double aValue1,
double aValue2)
286 return (aValue1 >= aValue2) ? TRUE : FALSE;
324 _muse_centroid_offset(
double *offset,
unsigned short axis,
325 const cpl_matrix *mdata,
const cpl_matrix *mwt,
326 const cpl_matrix *mwtxy,
const cpl_matrix *msgxy,
327 const cpl_matrix *mdg,
double sumg,
double sumgsqr,
328 double sgdg,
double sdg,
double sdgs,
329 double p,
double sigsqr)
335 if ((axis != 1) && (axis != 2)) {
343 sd = _cpl_matrix_sum_columns(mtmp);
346 cpl_matrix *_sd = _cpl_matrix_sum_rows(mtmp);
347 sd = cpl_matrix_transpose_create(_sd);
348 cpl_matrix_delete(_sd);
350 cpl_matrix_delete(mtmp);
354 sumd = _cpl_matrix_sum(mtmp);
355 cpl_matrix_delete(mtmp);
358 cpl_matrix_multiply(mtmp, sd);
359 sumgd = _cpl_matrix_sum(mtmp);
360 cpl_matrix_delete(mtmp);
363 cpl_matrix_multiply(mtmp, mdg);
364 sddg = _cpl_matrix_sum(mtmp);
365 cpl_matrix_delete(mtmp);
366 cpl_matrix_delete(sd);
370 double height = (sumgd - sumg * sumd / p) / (sumgsqr - (sumg * sumg) / p);
375 double skylvl = (sumd - height * sumg) / p;
376 double _offset = (sgdg - (sddg - sdg * (height * sumg + skylvl * p))) /
377 (height * sdgs / sigsqr);
379 if (isnan(_offset)) {
390 #define MUSE_SQR(x) ((x) * (x)) 456 const double *aRoundLimits,
const double *aSharpLimits)
458 cpl_ensure(aImage, CPL_ERROR_NULL_INPUT, NULL);
461 const int kMaxBox = 13;
464 const double kSharpLimits[2] = {0.2, 1.};
465 const double kRoundLimits[2] = {-1., 1.};
467 cpl_size nx = cpl_image_get_size_x(aImage);
468 cpl_size ny = cpl_image_get_size_y(aImage);
470 cpl_ensure(nx > 0 && ny > 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
471 cpl_ensure(nx % 2 == 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
472 cpl_ensure(aFwhm >= 0.5, CPL_ERROR_ILLEGAL_INPUT, NULL);
478 aRoundLimits = kRoundLimits;
482 aSharpLimits = kSharpLimits;
485 cpl_msg_debug(__func__,
"Settings: FWHM = %g, HMIN = %g, " 486 "Roundness limits = [%g, %g], Sharpness limits = [%g, %g]",
487 aFwhm, aHmin, aRoundLimits[0], aRoundLimits[1],
488 aSharpLimits[0], aSharpLimits[1]);
490 const double radius = CPL_MAX(0.637 * aFwhm, 2.001);
491 const double radsqr = MUSE_SQR(radius);
492 const double sigsqr = MUSE_SQR(aFwhm / 2.35482);
495 cpl_size nhalf = (cpl_size)CPL_MIN(radius, 0.5 * (kMaxBox - 1));
498 cpl_size nbox = 2 * nhalf + 1;
501 double *row2 = cpl_malloc(nbox *
sizeof *row2);
502 for (ipos = 0; ipos < nbox; ++ipos) {
503 row2[ipos] = MUSE_SQR(ipos - nhalf);
506 cpl_matrix *ckernel = cpl_matrix_new(nbox, nbox);
507 for (ipos = 0; ipos <= nhalf; ++ipos) {
509 for (jpos = 0; jpos < nbox; ++jpos) {
510 register double tmp = row2[jpos] + MUSE_SQR(ipos);
511 cpl_matrix_set(ckernel, nhalf - ipos, jpos, tmp);
512 cpl_matrix_set(ckernel, nhalf + ipos, jpos, tmp);
517 cpl_matrix *mask = cpl_matrix_new(nbox, nbox);
519 for (ipos = 0; ipos < nbox; ++ipos) {
521 for (jpos = 0; jpos < nbox; ++jpos) {
522 if (cpl_matrix_get(ckernel, ipos, jpos) > radsqr) {
523 cpl_matrix_set(mask, ipos, jpos, 0.);
526 cpl_matrix_set(mask, ipos, jpos, 1.);
532 _muse_condition_not_equal);
533 cpl_size npixels = cpl_array_get_size(positions);
535 cpl_array_delete(positions);
540 cpl_matrix_multiply_scalar(ckernel, -0.5 / sigsqr);
541 cpl_matrix_exponential(ckernel, CPL_MATH_E);
558 cpl_matrix *xwt = cpl_matrix_new(nbox, nbox);
560 for (ipos = 0; ipos < nbox; ++ipos) {
562 double tmp = nhalf - imaxabs(ipos - nhalf) + 1;
563 for(jpos = 0; jpos < nbox; ++jpos) {
564 cpl_matrix_set(xwt, jpos, ipos, tmp);
567 cpl_matrix *ywt = cpl_matrix_transpose_create(xwt);
568 cpl_matrix *wt = cpl_matrix_extract_row(xwt, 0);
569 double p = _cpl_matrix_sum(wt);
571 cpl_matrix *mtmp = NULL;
574 cpl_matrix *sgx = _cpl_matrix_sum_rows(mtmp);
575 cpl_matrix_delete(mtmp);
579 sgx = cpl_matrix_transpose_create(mtmp);
580 cpl_matrix_delete(mtmp);
583 cpl_matrix *sgy = _cpl_matrix_sum_columns(mtmp);
584 cpl_matrix_delete(mtmp);
587 double sumgx = _cpl_matrix_sum(wsgy);
590 double sumgsqy = _cpl_matrix_sum(mtmp);
591 cpl_matrix_delete(mtmp);
594 double sumgy = _cpl_matrix_sum(wsgx);
597 double sumgsqx = _cpl_matrix_sum(mtmp);
598 cpl_matrix_delete(mtmp);
601 cpl_matrix *vec = cpl_matrix_new(1, nbox);
602 for (ipos = 0; ipos < nbox; ++ipos) {
603 cpl_matrix_set(vec, 0, ipos, nhalf - ipos);
607 cpl_matrix_delete(vec);
614 cpl_matrix_delete(mtmp);
618 cpl_matrix_delete(mtmp);
621 double sdgdx = _cpl_matrix_sum(wdgdx);
622 cpl_matrix_delete(wdgdx);
625 double sdgdy = _cpl_matrix_sum(wdgdy);
626 cpl_matrix_delete(wdgdy);
629 double sdgdxs = _cpl_matrix_sum(wdgdxs);
630 cpl_matrix_delete(wdgdxs);
633 double sdgdys = _cpl_matrix_sum(wdgdys);
634 cpl_matrix_delete(wdgdys);
638 double sgdgdx = _cpl_matrix_sum(mtmp);
639 cpl_matrix_delete(mtmp);
640 cpl_matrix_delete(wsgy);
644 double sgdgdy = _cpl_matrix_sum(mtmp);
645 cpl_matrix_delete(mtmp);
646 cpl_matrix_delete(wsgx);
656 cpl_matrix_multiply(ckernel, mask);
658 double sumc = _cpl_matrix_sum(ckernel);
661 double sumcsq = -MUSE_SQR(sumc) / npixels + _cpl_matrix_sum(mtmp);
663 cpl_matrix_delete(mtmp);
665 cpl_matrix *c1 = cpl_matrix_new(1, nbox);
668 for (ipos = 0; ipos < nbox; ++ipos) {
669 double tmp = exp(-0.5 * row2[ipos] / sigsqr);
671 sumc1sq += MUSE_SQR(tmp);
672 cpl_matrix_set(c1, 0, ipos, tmp);
679 cpl_matrix_subtract_scalar(c1, sumc1);
680 cpl_matrix_divide_scalar(c1, sumc1sq);
682 cpl_matrix_subtract_scalar(ckernel, sumc);
683 cpl_matrix_divide_scalar(ckernel, sumcsq);
684 cpl_matrix_multiply(ckernel, mask);
687 cpl_msg_debug(__func__,
"Relative error computed from the FWHM: %g",
688 sqrt(_cpl_matrix_sum(mtmp)));
689 cpl_matrix_delete(mtmp);
692 cpl_msg_debug(__func__,
"Beginning convolution of image.");
695 cpl_matrix_delete(ckernel);
699 cpl_matrix_delete(c1);
700 cpl_matrix_delete(dgdy);
701 cpl_matrix_delete(dgdx);
702 cpl_matrix_delete(sgy);
703 cpl_matrix_delete(sgx);
704 cpl_matrix_delete(wt);
705 cpl_matrix_delete(ywt);
706 cpl_matrix_delete(xwt);
707 cpl_matrix_delete(mask);
709 cpl_error_set_message(__func__, CPL_ERROR_INCOMPATIBLE_INPUT,
710 "Convolution of the input image failed!");
714 #if DEBUG_MUSE_FINDSTARS > 0 715 cpl_image_save(himage,
"convolved_image_initial.fits", CPL_TYPE_DOUBLE, NULL,
721 double hmin = cpl_image_get_min(himage);
723 cpl_image_fill_window(himage, 1, 1, nhalf, ny, hmin);
724 cpl_image_fill_window(himage, nx - nhalf + 1, 1, nx, ny, hmin);
725 cpl_image_fill_window(himage, nhalf + 1, 1, nx - nhalf, nhalf, hmin);
726 cpl_image_fill_window(himage, nhalf + 1, ny - nhalf + 1, nx - nhalf, ny, hmin);
728 #if DEBUG_MUSE_FINDSTARS > 0 729 cpl_image_save(himage,
"convolved_image_padded.fits", CPL_TYPE_DOUBLE, NULL,
733 cpl_msg_debug(__func__,
"Finished convolution of image.");
736 cpl_matrix_set(mask, nhalf, nhalf, 0.);
738 npixels = cpl_array_get_size(positions);
741 cpl_matrix *offset = cpl_matrix_new(1, npixels);
742 for (ipos = 0; ipos < npixels; ++ipos) {
743 cpl_matrix_set(offset, 0 , ipos,
744 cpl_array_get_cplsize(positions, ipos, NULL));
747 cpl_array_delete(positions);
750 cpl_matrix *xx = cpl_matrix_duplicate(offset);
751 _cpl_matrix_modulo(xx, nbox);
753 mtmp = cpl_matrix_duplicate(xx);
754 cpl_matrix_multiply_scalar(mtmp, -1.);
755 cpl_matrix_add(offset, mtmp);
756 cpl_matrix_divide_scalar(offset, nbox);
757 cpl_matrix_add_scalar(offset, -nhalf);
758 cpl_matrix_delete(mtmp);
760 cpl_matrix_add_scalar(xx, -nhalf);
761 cpl_matrix_multiply_scalar(offset, nx);
762 cpl_matrix_add(offset, xx);
763 cpl_matrix_delete(xx);
767 cpl_matrix *hmatrix = cpl_matrix_wrap(ny, nx, cpl_image_get_data_double(himage));
770 cpl_msg_debug(__func__,
"Selecting pixels exceeding threshold value %.6g",
775 cpl_size nfound = cpl_array_get_size(positions);
777 cpl_array_delete(positions);
778 cpl_matrix_unwrap(hmatrix);
779 cpl_matrix_delete(offset);
780 cpl_image_delete(himage);
781 cpl_matrix_delete(c1);
782 cpl_matrix_delete(dgdy);
783 cpl_matrix_delete(dgdx);
784 cpl_matrix_delete(sgy);
785 cpl_matrix_delete(sgx);
786 cpl_matrix_delete(wt);
787 cpl_matrix_delete(ywt);
788 cpl_matrix_delete(xwt);
789 cpl_matrix_delete(mask);
791 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
792 "No image pixel value exceeds threshold value!");
796 cpl_msg_debug(__func__,
"Found %" CPL_SIZE_FORMAT
" pixels with values " 797 "above the threshold value %.6g", nfound, aHmin);
800 for (ipos = 0; ipos < npixels; ++ipos) {
805 cpl_array *atmp = cpl_array_duplicate(positions);
806 cpl_array_add_scalar(atmp, cpl_matrix_get(offset, 0, ipos));
809 cpl_array_delete(atmp);
812 cpl_size tsize = cpl_matrix_get_ncol(hoffset);
813 if (cpl_matrix_get_ncol(hindex) < tsize) {
814 tsize = cpl_matrix_get_ncol(hindex);
818 cpl_size jpos = tsize;
820 if (cpl_matrix_get(hindex, 0, jpos) < cpl_matrix_get(hoffset, 0, jpos)) {
821 cpl_array_set_invalid(positions, jpos);
828 cpl_matrix_delete(hoffset);
829 cpl_matrix_delete(hindex);
832 cpl_array_delete(positions);
833 cpl_matrix_unwrap(hmatrix);
834 cpl_matrix_delete(offset);
835 cpl_image_delete(himage);
836 cpl_matrix_delete(c1);
837 cpl_matrix_delete(dgdy);
838 cpl_matrix_delete(dgdx);
839 cpl_matrix_delete(sgy);
840 cpl_matrix_delete(sgx);
841 cpl_matrix_delete(wt);
842 cpl_matrix_delete(ywt);
843 cpl_matrix_delete(xwt);
844 cpl_matrix_delete(mask);
846 cpl_error_set_message(__func__, CPL_ERROR_DATA_NOT_FOUND,
847 "No local maximum exceeding the threshold value " 854 cpl_matrix_delete(offset);
857 cpl_table *_sources = cpl_table_new(cpl_array_get_size(positions));
858 #if DEBUG_MUSE_FINDSTARS > 0 859 cpl_table_new_column(_sources,
"xidx", CPL_TYPE_LONG_LONG);
860 cpl_table_new_column(_sources,
"yidx", CPL_TYPE_LONG_LONG);
862 cpl_table_new_column(_sources,
"X", CPL_TYPE_DOUBLE);
863 cpl_table_new_column(_sources,
"Y", CPL_TYPE_DOUBLE);
864 cpl_table_new_column(_sources,
"Flux", CPL_TYPE_DOUBLE);
865 cpl_table_new_column(_sources,
"Sharpness", CPL_TYPE_DOUBLE);
866 cpl_table_new_column(_sources,
"Roundness", CPL_TYPE_DOUBLE);
869 for (ipos = 0; ipos < cpl_table_get_nrow(_sources); ++ipos) {
870 cpl_size _offset = cpl_array_get_cplsize(positions, ipos, NULL);
871 cpl_size ix = _offset % nx;
872 cpl_size iy = (_offset - ix) / nx;
874 cpl_size imin = ix - nhalf + 1;
875 cpl_size imax = ix + nhalf + 1;
876 cpl_size jmin = iy - nhalf + 1;
877 cpl_size jmax = iy + nhalf + 1;
879 #if DEBUG_MUSE_FINDSTARS > 0 880 cpl_table_set_long_long(_sources,
"xidx", ipos, ix);
881 cpl_table_set_long_long(_sources,
"yidx", ipos, iy);
884 if ((imin < 1) || (jmin < 1) || (imax >= nx) || (jmax >= ny)) {
886 cpl_table_unselect_row(_sources, ipos);
890 double flux = cpl_matrix_get(hmatrix, iy, ix);
891 cpl_image *_image = cpl_image_extract(aImage, imin + 1, jmin + 1,
893 cpl_matrix *mimg = cpl_matrix_wrap(nbox, nbox,
894 cpl_image_get_data_double(_image));
898 double sharpness = (cpl_matrix_get(mimg, nhalf, nhalf) -
899 _cpl_matrix_sum(mtmp) / npixels) / flux;
900 cpl_matrix_delete(mtmp);
902 if ((sharpness < kSharpLimits[0]) || (sharpness > kSharpLimits[1])) {
903 cpl_matrix_unwrap(mimg);
904 cpl_image_delete(_image);
907 cpl_table_unselect_row(_sources, ipos);
912 mtmp = _cpl_matrix_sum_columns(mimg);
913 cpl_matrix_multiply(mtmp, c1);
914 double dx = _cpl_matrix_sum(mtmp);
915 cpl_matrix_delete(mtmp);
917 cpl_matrix *_mtmp = _cpl_matrix_sum_rows(mimg);
918 mtmp = cpl_matrix_transpose_create(_mtmp);
919 cpl_matrix_multiply(mtmp, c1);
920 double dy = _cpl_matrix_sum(mtmp);
921 cpl_matrix_delete(mtmp);
922 cpl_matrix_delete(_mtmp);
924 if ((dx <= 0.) || (dy <= 0.)) {
925 cpl_matrix_unwrap(mimg);
926 cpl_image_delete(_image);
929 cpl_table_unselect_row(_sources, ipos);
933 double roundness = 2. * (dx - dy) / (dx + dy);
934 if ((roundness < kRoundLimits[0]) || (roundness > kRoundLimits[1])) {
935 cpl_matrix_unwrap(mimg);
936 cpl_image_delete(_image);
939 cpl_table_unselect_row(_sources, ipos);
955 status = _muse_centroid_offset(&xoffset, 1, mimg, wt, ywt, sgy, dgdx,
956 sumgx, sumgsqy, sgdgdx, sdgdx, sdgdxs, p,
959 cpl_matrix_unwrap(mimg);
960 cpl_image_delete(_image);
961 cpl_table_unselect_row(_sources, ipos);
966 status = _muse_centroid_offset(&yoffset, 2, mimg, wt, xwt, sgx, dgdy,
967 sumgy, sumgsqx, sgdgdy, sdgdy, sdgdys, p,
970 cpl_matrix_unwrap(mimg);
971 cpl_image_delete(_image);
972 cpl_table_unselect_row(_sources, ipos);
977 if ((fabs(xoffset) >= nhalf) || (fabs(yoffset) >= nhalf)) {
978 cpl_matrix_unwrap(mimg);
979 cpl_image_delete(_image);
980 cpl_table_unselect_row(_sources, ipos);
983 cpl_matrix_unwrap(mimg);
984 cpl_image_delete(_image);
990 double xcenter = (ix + 1) + xoffset;
991 double ycenter = (iy + 1) + yoffset;
993 cpl_table_set_double(_sources,
"X", ipos, xcenter);
994 cpl_table_set_double(_sources,
"Y", ipos, ycenter);
995 cpl_table_set_double(_sources,
"Flux", ipos, flux);
996 cpl_table_set_double(_sources,
"Sharpness", ipos, sharpness);
997 cpl_table_set_double(_sources,
"Roundness", ipos, roundness);
1001 cpl_array_delete(positions);
1002 cpl_matrix_unwrap(hmatrix);
1003 cpl_image_delete(himage);
1004 cpl_matrix_delete(c1);
1005 cpl_matrix_delete(dgdy);
1006 cpl_matrix_delete(dgdx);
1007 cpl_matrix_delete(sgy);
1008 cpl_matrix_delete(sgx);
1009 cpl_matrix_delete(wt);
1010 cpl_matrix_delete(ywt);
1011 cpl_matrix_delete(xwt);
1012 cpl_matrix_delete(mask);
1015 #if DEBUG_MUSE_FINDSTARS > 0 1016 cpl_table_save(_sources, NULL, NULL,
"detections.fits", CPL_IO_CREATE);
1019 cpl_table *sources = cpl_table_extract_selected(_sources);
1020 cpl_size ncandidates = cpl_table_get_nrow(_sources);
1021 cpl_size nselected = cpl_table_get_nrow(sources);
1023 cpl_table_delete(_sources);
1025 #if DEBUG_MUSE_FINDSTARS > 0 1026 cpl_table_erase_column(sources,
"xidx");
1027 cpl_table_erase_column(sources,
"yidx");
1030 if (ncandidates == 0) {
1031 cpl_msg_debug(__func__,
"No candidate stars were found!");
1034 cpl_msg_debug(__func__,
"Total number of candidate stars %" CPL_SIZE_FORMAT
1035 "; no. selected candidates %" CPL_SIZE_FORMAT, ncandidates,
1039 if (nselected == 0) {
1040 cpl_table_delete(sources);
cpl_error_code muse_cplarray_erase_invalid(cpl_array *aArray)
Erase all invalid values from an array.
cpl_matrix * muse_cplmatrix_extract_selected(const cpl_matrix *aMatrix, const cpl_array *aIndices)
Extract the elements given by the index array.
cpl_matrix * muse_cplmatrix_multiply_create(const cpl_matrix *aMatrix1, const cpl_matrix *aMatrix2)
Compute the element-wise product of two matrices.
cpl_array * muse_cplmatrix_where(const cpl_matrix *aMatrix, double aValue, muse_cplmatrix_element_compare_func aCondition)
Select matrix elements according to a condition.
cpl_table * muse_find_stars(const cpl_image *aImage, double aHmin, double aFwhm, const double *aRoundLimits, const double *aSharpLimits)
Find positive brightness perturbations (i.e stars) in an image.
cpl_image * muse_convolve_image(const cpl_image *aImage, const cpl_matrix *aKernel)
Compute the convolution of an image with a kernel.