00001 #include "mex.h" /* must appear first */ 00002 #include <stdio.h> 00003 #include <string.h> 00004 #include <math.h> 00005 #include "mexhead.h" /* my mex defines */ 00006 #include "Doc/concomponent_docstring.h" /* autogenerated from this file */ 00007 00008 /************************************************************** 00009 00010 %concomponent: identify connected components 00011 % 00012 % [y,nr]=concomponent(x,nbr); 00013 % * identifies connected components of a binary labeling x, storing 00014 % the result as a like-sized image y. The number of regions is 00015 % optionally returned in nr. 00016 % * On-region components of y are in the range 1..nr. Inputs x 00017 % of 0 or NaN are treated as off-region, but passed intact through 00018 % to output region map as 0 or NaN. 00019 % * Either the 4-pixel neighborhood (N/S/E/W) template can be used, 00020 % or the 8-pixel neighborhood, which includes diagonal pixels. 00021 % Default is 8. 00022 % * This is implemented as a MEX file. 00023 % 00024 % Inputs: 00025 % real x(m,n); -- 0, 1, or NaN 00026 % opt int nbr = 8; -- 4 or 8 00027 % 00028 % Outputs: 00029 % int y(m,n); -- 0, NaN, or 1..nr 00030 % opt int nr; 00031 % 00032 % See Also: region_bb 00033 00034 % turmon may 2001 00035 00036 ****************************************************************/ 00037 /* Updated by -mex2pymex.py-ver1- on Wed Sep 23 16:55:51 2009 */ 00038 00039 /* standard boilerplate */ 00040 00041 #define NARGIN_MIN 1 /* min number of inputs */ 00042 #define NARGIN_MAX 2 /* max number of inputs */ 00043 #define NARGOUT_MIN 0 /* min number of output args */ 00044 #define NARGOUT_MAX 2 /* max number of output args */ 00045 00046 #define ARG_x 0 /* x = data */ 00047 #define ARG_nbr 1 /* nbr = neighbor template */ 00048 #define ARG_y 0 /* y = labels */ 00049 #define ARG_nr 1 /* region count */ 00050 00051 static const char *progname = "concomponent"; 00052 #define PROGNAME concomponent 00053 static const char *in_specs[NARGIN_MAX] = { 00054 "RM", 00055 "IS"}; 00056 static const char *in_names[NARGIN_MAX] = { 00057 "x", 00058 "nbr"}; 00059 static const char *out_names[NARGOUT_MAX] = {"y", "nr"}; 00060 00061 // this is just for brevity when compiled as a C library 00062 #define SHORTNAME ccp 00063 00064 /******************* Utilities ***************/ 00065 00066 /* 00067 * squeeze out unused region numbers from region map 00068 * returns final number of regions 00069 * This means recycling labels that turn out not to be used because 00070 * they are equivalent to other labels. 00071 * Algorithm is: scan the region table T, looking for minimal 00072 * regions (T[r] == r). Such regions are the prototype for 00073 * their class. When one is found, replace all entries in 00074 * the equivalence table equal to r (including entry r itself) 00075 * with the current count of the number of minimal regions r_now. 00076 * This amounts to relabeling all final outputs r with r_now. 00077 */ 00078 00079 static 00080 int 00081 squeeze_regions(int *T, /* table */ 00082 int Nr) /* number of valid entries */ 00083 00084 { 00085 int r, rp; /* region counters */ 00086 int r_now; /* current region */ 00087 00088 for (r_now = r = 1; r <= Nr; r++) 00089 if (T[r] == r) { 00090 /* current region is its own minimal region. 00091 * renumber it, and all regions mapping to it, 00092 * as r_now. 00093 * Actually, only do the renumbering if 00094 * it would result in a numbering change. 00095 */ 00096 if (r_now != r) 00097 for (rp = r; rp <= Nr; rp++) 00098 if (T[rp] == r) 00099 T[rp] = r_now; 00100 r_now++; /* update current region */ 00101 } 00102 return r_now-1; 00103 } 00104 00105 00106 /* 00107 * add the equivalance r1 == r2 to the table T 00108 * Before this equivalence was added, we knew that (r1,T[r1]) were 00109 * equivalent, and also that (r2,T[r2]) were equivalent. The added 00110 * fact that (r1,r2) are equivalent means that all four states 00111 * are equivalent. Their minimal class is the min z of the earlier 00112 * two minimal classes T[r1], T[r2]. 00113 * The way to signal the new equivalence is to replace each 00114 * instance of r1, r2, T[r1], or T[r2] in the minimal class list 00115 * by z. In some cases, we would know that no instance of one of 00116 * these would occur, but it is better to not separate into cases. 00117 */ 00118 static 00119 void 00120 add_equivalence(int *T, 00121 int Nr, 00122 int r1, 00123 int r2) 00124 { 00125 int z; /* new minimal region */ 00126 int s1, s2; /* old minimal regions */ 00127 int r; /* region counter */ 00128 00129 if (r1 == r2) return; /* trivial: nothing to do */ 00130 /* if r1, r2 already have same minimal class listed in T, 00131 they are equivalent and there is nothing to update */ 00132 if (T[r1] == T[r2]) return; 00133 /* printf("\tadding %4d <-> %4d\t(%4d %4d)\n", r1, r2, T[r1], T[r2]); */ 00134 /* the new minimal region for the equivalence class containing 00135 r1 and r2 is the smaller of the previous minimal regions */ 00136 z = T[r1] < T[r2] ? T[r1] : T[r2]; 00137 /* save these old minimal regions */ 00138 s1 = T[r1]; 00139 s2 = T[r2]; 00140 /* replace old instances of the older minimal regions 00141 Before this equivalence, we knew that (r1,T[r1]) */ 00142 /* NB: must go up to and including entry #Nr because table starts at 1 00143 However, can start at z rather than 1 because no table entry 00144 lower than z can be numbered r1, r2, s1, or s2, since z is 00145 smaller than them all: r < z => T[r] < z => T[r] < r1,r2,s1,s2. */ 00146 for (r = z; r <= Nr; r++) 00147 if (T[r] == r1 || 00148 T[r] == r2 || 00149 T[r] == s1 || 00150 T[r] == s2) 00151 T[r] = z; 00152 } 00153 00154 00155 /* simple utilities */ 00156 #define ISLEGAL(l,L) ((l>=0)&&(l<L)) /* for indexing: 0 <= l < L */ 00157 #define ActiveLabel(y) ((!isnan(y)) && ((y) > 0)) /* not NaN and not 0 */ 00158 00159 /* 00160 * concomponent: label connected components 00161 * - We assume x is in the range 1..K, or 0, or NaN. If 0 or NaN, the 00162 * corresponding pixel is excluded from the calculation, and its value 00163 * is propagated through. Otherwise, connected components are labeled 00164 * starting at 1. 00165 * - The algorithm is to scan through all pixels, putting a region number 00166 * in the corresponding entry of y if x is active. The current region 00167 * number is l_new. If the pixel is isolated (no previously-scanned 00168 * neighbors were labeled) then a fresh region number is chosen and l_new 00169 * is incremented. Otherwise, a neighbor's region number is used 00170 * ("recycling") 00171 * - Either the 4 or 8 neighbor template may be used. In case of the 00172 * 4-nbr template, two neighbors have been examined before the current 00173 * pixel, so we need to look at those two to recycle labels. In the 8- 00174 * nbr template, we look at the four prior pixels. 00175 * - The rub: there is more than one neighbor, so the neighbors' labels 00176 * can conflict. So we see that, in the first scan, we are really 00177 * identifying equivalence classes of labels. We keep a table T to keep 00178 * track of the equivalences. 00179 * - The table has one entry per active region-label. Regions start at 1 00180 * so the physical table has one entry more than the number of active 00181 * labels. The table starts out at an arbitrary size and is grown 00182 * if necessary, this is checked whenever a fresh region number is 00183 * chosen. 00184 * - The fundamental property of the table is: the entry for region 00185 * r contains the smallest-numbered ("minimal") region equivalent to r. 00186 * This makes T[r] unique. It implies that T[r] <= r. Equality means 00187 * that r is itself minimal -- such a region is the prototype for 00188 * its class since it was the first component uncovered in the scan. 00189 * Since T[r] is minimal, T[r1] == T[r2] iff r1 and r2 are 00190 * in the same equivalence class. 00191 * - T is initialized with T[r] = r, and whenever a neighbor-conflict 00192 * is discovered, an equivalence is added to T (see below). 00193 * - After the first scan, a preliminary labeling (correct up to 00194 * equivalences in T) is determined. Because we want the final 00195 * region numbers not to have gaps, we have to squeeze out the 00196 * "blank" entries in T. (E.g., if region 2 maps to region 1, then 00197 * region 2 will not appear in the output and region 3 (say) might 00198 * need to be renumbered 2.) 00199 * - Finally, the pixels are scanned in order and their numbers 00200 * updated to reflect the equivalences: just setting all labels 00201 * in an equivalence class to the number of the minimal class: 00202 * y[n][m] = T[y[n][m]]. 00203 */ 00204 static 00205 void 00206 concomponent( 00207 double **x, /* input image */ 00208 double **y, /* result: labeling */ 00209 double *n_reg, /* result: number of regions */ 00210 int n_nbr, /* number of neighbors */ 00211 int M, /* dims of image, labeling */ 00212 int N) 00213 00214 { 00215 void *mxCalloc(); 00216 /* offsets to neighbors that were already scanned in the order we use 00217 (-1,0)=up; (0,-1)=left; (-1,-1)=left+up; (1,-1)=left+down 00218 note, by stopping after the first two neighbors, the 4-pixel 00219 neighbor template (up/left/down/right) can be used painlessly. */ 00220 int delta_m[] = {-1,0,-1,1}; /* previous neighbor offsets */ 00221 int delta_n[] = {0,-1,-1,-1}; /* previous neighbor offsets */ 00222 int m, n; /* loop counters */ 00223 int nbr; /* loop counter */ 00224 int mp, np; /* position of neighbor pixel */ 00225 int l_new = 0; /* current label counter */ 00226 int l_use; /* label to use */ 00227 int r; /* region counter */ 00228 double *box; /* convenience pointer */ 00229 int *T; /* minimal region table */ 00230 int r_max = 1024; /* starting point for #regions in table */ 00231 00232 /* initialize table of minimal regions */ 00233 T = (int *) mxCalloc(r_max, sizeof(*T)); 00234 for (r = 0; r < r_max; r++) T[r] = r; /* regions are self-linked */ 00235 /* Step 1: 00236 * loop over all pixels, finding contiguous regions 00237 */ 00238 for (n = 0; n < N; n++) 00239 for (m = 0; m < M; m++) { 00240 /* skip non-object pixels (0 or NaN) */ 00241 if (!ActiveLabel(x[n][m])) { 00242 y[n][m] = x[n][m]; /* NB: 0/NaN values are copied through */ 00243 continue; 00244 } 00245 /* else, are on an object: must label it */ 00246 /* first, search neighbors to recycle labels and find conflicts */ 00247 /* 4-nbr template -> 2 prior neighbors, 8-nbr template -> 4 priors */ 00248 l_use = 0; /* assume un-labeled */ 00249 for (nbr = 0; nbr < n_nbr/2; nbr++) { 00250 /* neighboring pixel coordinates */ 00251 mp = m + delta_m[nbr]; 00252 np = n + delta_n[nbr]; 00253 /* check the neighbor label */ 00254 if (!ISLEGAL(mp,M) || !ISLEGAL(np,N) || !ActiveLabel(y[np][mp])) 00255 continue; /* skip bad indexes and unlabeled pixels */ 00256 /* at this point, the neighbor pixel is valid and on-object */ 00257 /* 1: locate an already-labeled neighbor */ 00258 if (!l_use) { 00259 l_use = y[np][mp]; /* this will be the pixel label */ 00260 continue; /* must continue, to find conflicting labels */ 00261 } 00262 /* 2: search for label conflicts */ 00263 if (l_use != y[np][mp]) { 00264 /* pixel label conflicts with another adjacent label: 00265 enter the conflict as a region equivalence in the table T. 00266 This table currently has l_new entries. */ 00267 /* printf("(%d,%d): %d <-> %g\n", n, m, l_use, y[np][mp]); */ 00268 add_equivalence(T, l_new, l_use, (int) y[np][mp]); 00269 continue; 00270 } 00271 } 00272 /* printf("(%d,%d): <- %d\n", n, m, l_use); */ 00273 /* assign pixel label */ 00274 if (l_use) 00275 y[n][m] = l_use; /* already found label */ 00276 else { 00277 y[n][m] = ++l_new; /* create new label */ 00278 /* check if table should be lengthened. Since the table starts at 00279 one, and its next entry will be l_new+1, we require that 00280 T[l_new+1] be valid. */ 00281 if ((l_new + 1) >= r_max) { 00282 /* must expand region table */ 00283 int *Tp; /* temporary for new table */ 00284 r_max *= 4; /* increase size */ 00285 Tp = (int *) mxCalloc(r_max, sizeof(*T)); 00286 for (r = 0; r < r_max; r++) Tp[r] = r; /* start with T[r] = r */ 00287 /* copy former table to new table */ 00288 memcpy((void *) Tp, (void *) T, (r_max/4) * sizeof(int)); 00289 mxFree(T); /* dump old table */ 00290 T = Tp; /* install new table */ 00291 } 00292 } 00293 } 00294 /* Step 2: 00295 * squeeze out unused region numbers from region map 00296 */ 00297 *n_reg = (double) squeeze_regions(T, l_new); 00298 /* Step 3: 00299 * loop over all pixels, updating labels 00300 */ 00301 for (n = 0; n < N; n++) 00302 for (m = 0; m < M; m++) { 00303 if (ActiveLabel(y[n][m])) 00304 y[n][m] = T[(int) (y[n][m])]; 00305 } 00306 mxFree(T); 00307 return; 00308 } 00309 00310 00311 /* 00312 * Gateway routine 00313 */ 00314 #ifdef StaticP /* undefined under mex */ 00315 StaticP 00316 #endif 00317 void 00318 mexFunction( 00319 int nlhs, 00320 mxArray *plhs[], 00321 int nrhs, 00322 const mxArray *prhs[]) 00323 { 00324 int m, n; /* size of x */ 00325 mxArray *plhs_bb; /* slot to hold bb matrix structure */ 00326 double nr_result; /* slot for nr number */ 00327 double **x2, **y2; /* 2d indexing */ 00328 int nr_wanted; /* is the nr output wanted */ 00329 int n_reg; /* number of regions found */ 00330 int n_nbr; /* neighbor template */ 00331 char errstr[256]; 00332 00333 /* Hook for introspection (function signature, docstring) */ 00334 if (nrhs < 0) { 00335 plhs[0] = mxt_PackSignature((mxt_Signature) (-nrhs), 00336 NARGIN_MIN, NARGIN_MAX, 00337 NARGOUT_MIN, NARGOUT_MAX, 00338 in_names, in_specs, out_names, docstring); 00339 return; 00340 } 00341 /* 00342 * check args 00343 */ 00344 if ((nrhs < NARGIN_MIN) || (nrhs > NARGIN_MAX)) 00345 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00346 "%s: Expect %d <= input args <= %d", 00347 progname, NARGIN_MIN, NARGIN_MAX), errstr)); 00348 if ((nlhs < NARGOUT_MIN) || (nlhs > NARGOUT_MAX)) 00349 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00350 "%s: Expect %d <= output args <= %d", 00351 progname, NARGOUT_MIN, NARGOUT_MAX), errstr)); 00352 mexargparse(nrhs, prhs, in_names, in_specs, NULL, progname); 00353 00354 /* 00355 * optional args 00356 */ 00357 n_nbr = (nrhs <= ARG_nbr) ? 8 : mxGetScalar(prhs[ARG_nbr]); 00358 if (n_nbr != 4 && n_nbr != 8) 00359 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00360 00361 "%s: Expect neighbor template = 4 or 8, got %d", 00362 progname, n_nbr), errstr)); 00363 00364 /* 00365 * create space for output 00366 */ 00367 /* 1: find how big it must be */ 00368 m = mxGetM(prhs[ARG_x]); 00369 n = mxGetN(prhs[ARG_x]); 00370 00371 /* 2: make the space */ 00372 /* first, for y */ 00373 /* paradoxically works even if nlhs == 0, in which case strictly 00374 speaking plhs[0] need not exist (i.e. plhs == NULL in this case) 00375 MATLAB seems to always have plhs non-null to allow for transmission 00376 of the result via 'ans' */ 00377 plhs[ARG_y] = mxCreateDoubleMatrix(m, n, mxREAL); 00378 /* then, for nr */ 00379 nr_wanted = (nlhs == (ARG_nr+1)); /* if nr output arg supplied */ 00380 00381 /* 00382 * do the computation 00383 */ 00384 /* set up ordinary 2-d pointers */ 00385 x2 = mxt_make_matrix2(prhs[ARG_x], -1, -1, 0.0), 00386 y2 = mxt_make_matrix2(plhs[ARG_y], -1, -1, 0.0), 00387 concomponent(x2, y2, 00388 /* region number */ 00389 &nr_result, 00390 /* sizes */ 00391 n_nbr, 00392 m, n); 00393 mxFree(x2); 00394 mxFree(y2); 00395 00396 /* plug nr in to its slot if wanted (else, there's no slot!) */ 00397 if (nr_wanted) { 00398 plhs[ARG_nr] = mxCreateDoubleMatrix(1, 1, mxREAL); 00399 *(mxGetPr(plhs[ARG_nr])) = nr_result; 00400 } 00401 } 00402 00403 00404 /* Hook for generic tail matter */ 00405 #ifdef MEX2C_TAIL_HOOK 00406 #include "mex2c_tail.h" 00407 #endif 00408