#include "mex.h" #include #include #include #include #include #ifndef PI #define PI 3.14159265358979 #endif /* changed const int * to const void * DMT 8/22/07 */ int ii_cmp(const void * a, const void * b); /* changed const int * to const void * DMT 8/22/07 */ int jj_cmp(const void * a, const void * b); void merge_regions(/* Input */ int *nn, /* Number of bordering voxels per pair of regions. */ double *pp, /* Sum of phase differences along border between regions of pair. */ int nc, /* Length of list of region pairs. */ int *rs, /* Size (voxels) per region. */ int nr, /* Number of regions. */ /* Output */ int *rw); /* Multiple of 2pi wrap for each region. */ int *binsearch(int *list, int *ia, int n, int post, int *length); int linsearch(int *list, int *ia, int n, int post); int my_round(double iv); void sillysort(int *ii_ia, int *jj_ia, int n); /* Declare arrays of row and column indicies into connectogram matrices as module global to allow us to sort on them and create index-arrays using stdlib function qsort. It's not my finest hour. */ static int *ii=NULL; static int *jj=NULL; static double *cc=NULL; /* changed const int * to const void * DMT 8/22/07 */ int ii_cmp(const void * a, const void * b) { /* if (jj[((int) *a)] > jj[((int) *b)]) {return(1);} else if (jj[((int) *a)] == jj[((int) *b)]) {return(0);} else {return(-1);} Replaced DMT 8/22/07 */ if (ii[(*(int*)a)] > ii[(*(int*)b)]) {return(1);} else if (ii[(*(int*) a)] == ii[(*(int*) b)]) {return(0);} else {return(-1);} } /* changed const int * to const void * DMT 8/22/07 */ int jj_cmp(const void * a, const void * b) { /* if (jj[((int) *a)] > jj[((int) *b)]) {return(1);} else if (jj[((int) *a)] == jj[((int) *b)]) {return(0);} else {return(-1);} Replaced DMT 8/22/07 */ if (jj[(*(int*)a)] > jj[(*(int*)b)]) {return(1);} else if (jj[(*(int*) a)] == jj[(*(int*) b)]) {return(0);} else {return(-1);} } int my_round(double iv) { if (iv > 0.0) {return((int) (iv+0.5));} else if (iv < 0.0) {return((int) (iv-0.5));} else return(0); } void merge_regions(/* Input */ int *nn, /* Number of bordering voxels per pair of regions. */ double *pp, /* Sum of phase differences along border between regions of pair. */ int nc, /* Length of list of region pairs. */ int *rs, /* Size (voxels) per region. */ int nr, /* Number of regions. */ /* Output */ int *rw) /* Multiple of 2pi wrap for each region. */ { double k=0.0; /* Average multiple of 2pi phase difference between regions. */ double *cc=NULL; /* Array of "mis-wrap-costs" for each connection. */ double maxcc = -FLT_MAX; /* Current maximum cost. */ int l=0; /* Wrap when merging olbl with nlbl. */ int i=0, j=0, mi=0; /* Some indicies. */ int ci=0, ci2=0; /* Indexes into ii, jj, nn, pp and cc. */ int sign1=1, sign2=1; /* Used to keep track of sign changes when wrapping. */ int cnc=0; /* Current remaining no of connections. */ int ocnc=0; /* Remaining no. of connections at start of current iteration. */ int row=0, col=0; /* Row and column (two region labels) for current pair to merge. */ int olbl=0; /* Label numbers for current pair to */ int nlbl=0; /* merge such that nlbl+olbl->nlbl. */ int olbli_n=0; /* Number of instances of olbl in row-list. */ int olblj_n=0; /* Number of instances of olbl in column-list. */ int *olbli=NULL; /* List of indexes into ii such that ii[olbli[0:olbli_n-1]]==olbl */ int *olblj=NULL; /* List of indexes into jj such that jj[olblj[0:olblj_n-1]]==olbl */ int *mlbli_n=NULL; /* Number of instances of nlbl in row-list. */ int *mlblj_n=NULL; /* Number of instances of nlbl in column-list. */ int **mlbli=NULL; /* List of indexes into ii such that ii[nlbli[0:nlbli_n-1]]==nlbl */ int **mlblj=NULL; /* List of indexes into jj such that jj[nlblj[0:nlblj_n-1]]==nlbl */ int *ii_ia=NULL, *jj_ia=NULL; /* Index arrays for ii and jj. */ int *eqlist=NULL; /* Equivalence list, indicating the region */ /* i+1 is equivalent to eqlist[i] */ /* Allocate, initialise and sort index-arrays for ii (row-array) and jj (column array) such that ii[ii_ia[:]] is sorted in ascending order as is jj[jj_ia[:]]. */ ii_ia = (int *) mxCalloc(nc,sizeof(int)); jj_ia = (int *) mxCalloc(nc,sizeof(int)); for (i=0; i maxcc) {maxcc=cc[j]; mi=j;} } /* Find row and column of next pair to merge. */ row = ii[mi]; col = jj[mi]; /* Determine which label to give the merged region and update region size stats. */ nlbl = (rs[row-1] > rs[col-1]) ? row : col; olbl = (nlbl == row) ? col : row; rs[nlbl-1] = rs[row-1] + rs[col-1]; rs[olbl-1] = 0; /* Determine if region should be wrapped before merging. */ l = my_round(-pp[mi]/(2.0*PI*nn[mi])); /* Set wrapping for region olbl (and equivalent) in list, and update equivalence list. */ if (nlbl == row) {sign1 = -1;} else {sign1 = 1;} for (j=0; j nlbl) /* Search in column list. */ { mlbli[j] = binsearch(jj,jj_ia,cnc,jj[ci],&(mlbli_n[j])); } else /* Paired with nlbl, skip it. */ { mlbli[j] = NULL; mlbli_n[j] = 0; } } for (j=0; j nlbl) /* Search in column list. */ { mlblj[j] = binsearch(jj,jj_ia,cnc,ii[ci],&(mlblj_n[j])); } else /* Paired with nlbl, skip it. */ { mlblj[j] = NULL; mlblj_n[j] = 0; } } /* For each of these pairs we can consider three possible cases 1. It is paired with the new label, and should be deleted. 2. It is paired with a label that is also in a pair with the new label. In this case the stats for the pair 'new label'-label should be updated and 'old label'-label be deleted. 3. It is paired with a label that is NOT in a pair with the new label. In this case the stats for 'old label'-label should be transferred to a 'new label'-label pair and the 'old label'-label should be deleted. */ /* First go through all instances where olbl is row index. */ if (olbli) { sign2 = 1; for (j=0; jwe know where to look. */ else if ((jj[ci] < nlbl) && mlbli[j] && ((ci2=linsearch(jj,mlbli[j],mlbli_n[j],nlbl))>-1)) /* We found the label jj[ci] in the row-list matched with nlbl in the column list. Since olbl was in the row-list, this means we should sign-reverse sum of differences stats. */ { if (l) {pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci];} nn[ci2] += nn[ci]; pp[ci2] -= pp[ci]; /* Third sign reversal. */ k = -pp[ci2]/(2.0*PI*nn[ci2]); cc[ci2] = nn[ci2]*(0.5-fabs(k-floor(k+0.5))); nn[ci] = 0; /* Delete */ pp[ci] = 0.0; cc[ci] = 0.0; ii[ci] = jj[ci] = INT_MAX; cnc--; } else if ((jj[ci] > nlbl) && mlbli[j] && ((ci2=linsearch(ii,mlbli[j],mlbli_n[j],nlbl))>-1)) /* We found label jj[ci] in the column-list matched with nlbl in the row list. Since olbl was also in the row-list, this means we should just add to sum of differences stats. */ { if (l) {pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci];} nn[ci2] += nn[ci]; pp[ci2] += pp[ci]; k = -pp[ci2]/(2.0*PI*nn[ci2]); cc[ci2] = nn[ci2]*(0.5-fabs(k-floor(k+0.5))); nn[ci] = 0; /* Delete */ pp[ci] = 0.0; cc[ci] = 0.0; ii[ci] = jj[ci] = INT_MAX; cnc--; } else /* So, this label has not been mixed up with nlbl before. Well, it is now. */ { if (jj[ci] < nlbl) { if (l) { pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci]; k = -pp[ci]/(2.0*PI*nn[ci]); cc[ci] = nn[ci]*(0.5-fabs(k-floor(k+0.5))); } ii[ci] = jj[ci]; jj[ci] = nlbl; pp[ci] *= -1.0; } else { if (l) { pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci]; k = -pp[ci]/(2.0*PI*nn[ci]); cc[ci] = nn[ci]*(0.5-fabs(k-floor(k+0.5))); } ii[ci] = nlbl; } } } } /* Now go through list where olbl is column index. */ if (olblj) { sign2 = -1; for (j=0; jwe know where to look. */ else if ((ii[ci] < nlbl) && mlblj[j] && ((ci2=linsearch(jj,mlblj[j],mlblj_n[j],nlbl))>-1)) /* We found the label ii[ci] in the row-list matched with nlbl in the column list. Since olbl was in the column-list, this means we should just add sum of differences stats. */ { if (l) {pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci];} nn[ci2] += nn[ci]; pp[ci2] += pp[ci]; k = -pp[ci2]/(2.0*PI*nn[ci2]); cc[ci2] = nn[ci2]*(0.5-fabs(k-floor(k+0.5))); nn[ci] = 0; /* Delete */ pp[ci] = 0.0; cc[ci] = 0.0; ii[ci] = jj[ci] = INT_MAX; cnc--; } else if ((ii[ci] > nlbl) && mlblj[j] && ((ci2=linsearch(ii,mlblj[j],mlblj_n[j],nlbl))>-1)) /* We found label ii[ci] in the column-list matched with nlbl in the row list. Since olbl was also in the column-list, this means we should sign-reverse when adding to sum of differences stats. */ { if (l) {pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci];} nn[ci2] += nn[ci]; pp[ci2] -= pp[ci]; /* Third sign reversal. */ k = -pp[ci2]/(2.0*PI*nn[ci2]); cc[ci2] = nn[ci2]*(0.5-fabs(k-floor(k+0.5))); nn[ci] = 0; /* Delete */ pp[ci] = 0.0; cc[ci] = 0.0; ii[ci] = jj[ci] = INT_MAX; cnc--; } else /* So, this label has not been mixed up with nlbl before. Well, it is now. */ { if (ii[ci] < nlbl) { if (l) { pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci]; k = -pp[ci]/(2.0*PI*nn[ci]); cc[ci] = nn[ci]*(0.5-fabs(k-floor(k+0.5))); } jj[ci] = nlbl; } else { if (l) { pp[ci] = pp[ci] + sign1*sign2*l*2.0*PI*nn[ci]; k = -pp[ci]/(2.0*PI*nn[ci]); cc[ci] = nn[ci]*(0.5-fabs(k-floor(k+0.5))); } jj[ci] = ii[ci]; ii[ci] = nlbl; pp[ci] *= -1.0; } } } } if (!cnc) {break;} /* No more connections, we're done. */ /* Resort index arrays ii_ia and jj_ia. Connections that are no longer valid have been given ii and jj values of INT_MAX, which means they will be out at the end of the ii_ia and jj_ia arrays, and will not be considered in subsequent iterations. */ if ((ocnc-cnc) == 1) /* A large proportion of mergings involve merging of regions that have connections only with the current nlbl. Clearly we can do much better than a full quicksort for those cases. */ { sillysort(ii_ia,jj_ia,ocnc); } else { qsort(ii_ia,ocnc,sizeof(int),ii_cmp); qsort(jj_ia,ocnc,sizeof(int),jj_cmp); } /* return; */ } mxFree(cc); mxFree(ii_ia); mxFree(jj_ia); mxFree(mlbli_n); mxFree(mlblj_n); mxFree(mlbli); mxFree(mlblj); return; } /* ** Quick and dirty routine to sort lists when there is only one ** (meaning that there is one INT_MAX wrong sorted in each ** index array) element sorted wrong. **It is all very primitive. A slightly more informative name ** would be "supersillysort", though a tad bit long. **/ void sillysort(int *ii_ia, int *jj_ia, int n) { int i=0, j=0; int tmp=0; for (i=0; i 1) { mp = ll + (ul-ll) / 2; if (list[ia[mp]] == post) {ll=mp; break;} else if (list[ia[mp]] < post) {ll=mp;} else {ul=mp;} } if ((ll<0) || (list[ia[ll]] != post)) {(*length)=0; return(NULL);} else { while (ll && (list[ia[ll-1]] == post)) {ll--;} } (*length)=0; while (((ll+(*length)) < n) && list[ia[ll+(*length)]] == post) {(*length)++;} return(&(ia[ll])); } /* ** ** Routine to do linear search for single instance of item "post". ** */ int linsearch(int *list, int *ia, int n, int post) { int i=0; for (i=0; i 3)) { mexErrMsgTxt("pm_merge_regions: pm must be 2 or 3-dimensional"); } cdim = mxGetDimensions(prhs[0]); pm = mxGetPr(prhs[0]); /* Get image of labels. */ if (!mxIsNumeric(prhs[1]) || mxIsComplex(prhs[1]) || mxIsSparse(prhs[1]) || !mxIsDouble(prhs[1])) { mexErrMsgTxt("pm_merge_regions: lima must be numeric, real, full and double"); } lima_ndim = mxGetNumberOfDimensions(prhs[1]); if (lima_ndim != ndim) { mexErrMsgTxt("pm_merge_regions: pm and lima must have same dimensionality"); } lima_cdim = mxGetDimensions(prhs[1]); for (i=0; i