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/makemrfdiscwts_docstring.h" /* autogenerated from this file */ 00007 00008 /************************************************************** 00009 00010 %makemrfdiscwts make distance metric for mrf segmentation 00011 % 00012 % dist=makemrfdiscwts(n,del,ctr,rho,mode,ell) 00013 % * Given image sizes n, and neighborhood size del, make a distance 00014 % metric array dist, such that dist(:,m1,n1) is the distance-to- 00015 % neighbors of the site m1,n1, where 1 <= m1 <= n(1) and 1 <= n1 <= n(2). 00016 % * Size of del = 3 corresponds to a 3x3 neighborhood around each site. 00017 % * The distance is computed for a disk of center ctr(1:2) and radius 00018 % ctr(3); ctr(1) corresponds to x or n1 and ctr(2) to y or m1. 00019 % ctr(1:2) = [1 1] corresponds to the first pixel. 00020 % * Thus, for instance, if Cx, Cy, r is the MDI center from mdidisk(), 00021 % we would specify ctr=[Cx Cy r]. Note that ctr(1) and n(2) correspond 00022 % to x, while ctr(2) and n(1) correspond to y. 00023 % * Note that p-angle and b-angle are not needed by this routine. 00024 % For simplicity, a 5-tuple containing these as entries 4 and 5 00025 % (a `geom' vector) can be given for ctr, and the tail will be ignored. 00026 % * The distance scale factor is rho. Zero corresponds to uniform 00027 % distances (all one) and large positive values correspond to 00028 % separation-sensitive distances. 100 (20..200) is a typical value 00029 % for images of radius 500. To gain more insight, the raw distances 00030 % (theta below) are returned when rho=NaN is given. A histogram 00031 % of theta values relative to exp(-theta*rho) will show whether most 00032 % theta's land in the linear part of the exponential. 00033 % * If rho < 0 is given, the scale factor is taken to be abs(rho), but 00034 % the distances are rescaled so that the overall max equals 1. Otherwise, 00035 % the distances diminish across the entire disk as rho increases. 00036 % * More explicitly, consider two points (s, s') within the 2d disk. 00037 % They map to points on the unit sphere, say 00038 % s = (x y z), s' = (x' y' z'), 00039 % The angle theta (>0) between them is found, and the distance 00040 % is returned as: 00041 % dist = exp(-theta*rho) 00042 % Because theta is very close to zero, the code in fact uses the chord 00043 % distance rather than the arc distance between the two points. 00044 % * Because the distance is symmetric, dist only includes distances 00045 % between sites s,s' where s' is less than s. This is the case if 00046 % the pixel corresponding to s' comes before s in the memory footprint 00047 % of an image. 00048 % * Elliptical discs are allowed for. In this case, ctr(3) is the 00049 % major axis semidiameter, and ell(1) is that of the minor axis. 00050 % ell(2) is the counterclockwise rotation, in degrees, of the major 00051 % axis off of the "m" or "y" axis. If not given, ell(1)=ctr(3) and 00052 % ell(2)=0. The implied condition ell(1) <= ctr(3) is not required 00053 % by the code. 00054 % * If either s or s' is off-disk, zero is put in the corresponding 00055 % distance value. This is required by mrf_segment_wts. 00056 % * The required mode string switches between sesw (mode = 'sesw') 00057 % or transposed (mode = 'sene') pixel ordering, which works as follows. 00058 % * The normal HMI (and normal MDI) pixel ordering starts in the 00059 % southeast corner, and the first scan line of pixels runs toward the 00060 % southwest corner. This is `sesw' ordering. The transposed ordering 00061 % is `sene'; this ordering is what we used for the JPL MDI processing. 00062 % This is implemented via internal stride parameters. 00063 % * This is implemented as a MEX file. It agrees with makemrfdiscwts2.m 00064 % 00065 % Inputs: 00066 % int n(1) or (2); -- if n is scalar, assume a square image 00067 % int del; 00068 % real ctr(3) or ctr(4) or ctr(5); 00069 % real rho; 00070 % string mode; 00071 % opt real ell(2) = [ctr(3) 0]; 00072 % 00073 % Outputs: 00074 % real dist ((del*del-1)/2,n(1),n(2)); 00075 % 00076 % See Also: makemrfdiscwts2, mrf_segment_wts 00077 00078 % turmon oct 2006 00079 00080 ****************************************************************/ 00081 00082 /* standard boilerplate */ 00083 00084 #define NARGIN_MIN 5 /* min number of inputs */ 00085 #define NARGIN_MAX 6 /* max number of inputs */ 00086 #define NARGOUT_MIN 0 /* min number of output args */ 00087 #define NARGOUT_MAX 1 /* max number of output args */ 00088 00089 #define ARG_n 0 00090 #define ARG_del 1 00091 #define ARG_ctr 2 00092 #define ARG_rho 3 00093 #define ARG_mode 4 00094 #define ARG_ell 5 00095 00096 #define ARG_dist 0 // returned distance array 00097 00098 static const char *progname = "makemrfdiscwts"; 00099 #define PROGNAME makemrfdiscwts 00100 static const char *in_specs[NARGIN_MAX] = { 00101 "RS|RV(2)", 00102 "RS(1)", 00103 "RV(3)|RV(4)|RV(5)", 00104 "RS(1)", 00105 "SV", 00106 "RV(2)" }; 00107 static const char *in_names[NARGIN_MAX] = { 00108 "n", 00109 "del", 00110 "ctr", 00111 "rho", 00112 "mode", 00113 "ell"}; 00114 static const char *out_names[NARGOUT_MAX] = {"dist"}; 00115 00116 // defined just for brevity in a generated #include file 00117 #define SHORTNAME mdw 00118 00119 /* 00120 * extracts various (OK, 1) mode parameters from a free-form string 00121 * 00122 * returns 0 if the string was OK, an explanatory tag otherwise 00123 */ 00124 static 00125 char * 00126 extract_mode(char *mode, int *Msesw) 00127 { 00128 char *word; 00129 char *sep = ", \t"; 00130 00131 if (!mode) return "null"; /* should not happen */ 00132 for (word = strtok(mode, sep); word; word = strtok(NULL, sep)) { 00133 /* sesw, or transposed */ 00134 if (strcasecmp(word, "sesw" ) == 0) *Msesw = 1; 00135 else if (strcasecmp(word, "sene" ) == 0) *Msesw = 0; 00136 /* unrecognized word: not OK */ 00137 else return word; 00138 } 00139 return 0; /* OK */ 00140 } 00141 00142 00143 /* simple utilities */ 00144 #define ISLEGAL(l,L) ((l>=0)&&(l<L)) /* for indexing: 0 <= l < L */ 00145 #define ActiveLabel(y) ((!isnan(y)) && ((y) > 0)) /* not NaN and not 0 */ 00146 #define DIST_BLANK 0 /* distance value when s or s' is off-disk */ 00147 00148 /* 00149 * makedist: compose distance array 00150 * 00151 * Regarding rotation, the viewpoint taken here is that a true disc 00152 * has been stretched and rotated by the imaging process. Thus, 00153 * to find the distance of sites s and s', first their "true" 00154 * positions are recovered and then the distance is taken between 00155 * the two sites on an the sphere in which they are "truly" embedded. 00156 * 00157 * In particular, we do not have the viewpoint that the two sites are 00158 * embedded in a 3-d ellipsoid which has been projected into a 2-d ellipse. 00159 * So, we do not take the view that the chord distance that we are finding 00160 * is a chord on an ellipsoid. It is a chord on a sphere. These two 00161 * viewpoints may be related in some easy way, or may be the same, but 00162 * it's not obvious to me and I have not checked. 00163 */ 00164 static 00165 int 00166 makedist( 00167 double *d, /* distance output */ 00168 double rho, 00169 int raw, /* output raw distance only? */ 00170 int rescale, /* rescale d so max(*d) == 1 ? */ 00171 double xcen, 00172 double ycen, 00173 double Rm, /* radii */ 00174 double Rn, 00175 double a0, /* major-axis rotation -- NOT p-angle */ 00176 int del, 00177 // switch from m,n to x,y here 00178 // strides have been in x,y -- but MRF code uses m,n 00179 // trying to be compatible with both 00180 int maxx, // dims of eventual image (y/m) 00181 int maxy, // x/n 00182 int strx, // stride along x/n, units of doubles 00183 int stry // stride along y/m 00184 ) 00185 00186 { 00187 double *d0; /* position in d list */ 00188 int m, n, mp, np; /* loop counters */ 00189 int nu; /* neighbor loop counter */ 00190 double x, y, z; /* coords of s */ 00191 double xp, yp, zp; /* coords of s' */ 00192 double x0, y0; /* temp variables */ 00193 double theta; /* angle from s -> s' */ 00194 double filler; /* blank fill value */ 00195 double dmax; /* max *d so far */ 00196 const int delhalf = (del-1)/2; /* neighbors on either side */ 00197 const int del2half=(del*del-1)/2; /* number of neighbors (~del^2/2) */ 00198 const double RmInv = 1/Rm; 00199 const double RnInv = 1/Rn; 00200 const double Ca0 = cos(a0); 00201 const double Sa0 = sin(a0); 00202 00203 /* setup */ 00204 dmax = 0; /* max so far is zero */ 00205 if (rescale) 00206 filler = mxt_getnand(); /* out of range d filled with NaN for now */ 00207 else 00208 filler = DIST_BLANK; /* out of range d get final fill value */ 00209 /* 00210 * loop over s 00211 * note, m is like y, n is like x 00212 */ 00213 for (m = 0; m < maxy; m++) 00214 for (n = 0; n < maxx; n++) { 00215 d0 = d + (n*strx+m*stry)*del2half; /* head of d list for (m,n) site */ 00216 /* find s = x,y,z in unit coordinates */ 00217 x0 = n - xcen; /* offsets from center */ 00218 y0 = m - ycen; 00219 x = ( x0 * Ca0 + y0 * Sa0) * RnInv; /* rotate and scale */ 00220 y = (-x0 * Sa0 + y0 * Ca0) * RmInv; 00221 z = x*x + y*y; /* intermediate result */ 00222 /* off-disc check for s */ 00223 if (z > 1) { 00224 for (nu = 0; nu < del2half; nu++) 00225 d0[nu] = DIST_BLANK; /* set all nbrs to off-disk */ 00226 continue; /* i.e., next s = (m,n) */ 00227 } 00228 z = sqrt(1 - z); /* this is the real z */ 00229 /* (x,y,z) is now a position on the sphere where s lives */ 00230 00231 /* loop over s', keeping track of nbr count nu also */ 00232 /* note: this is where the neighbor indexing is critical 00233 * eg, for del=3, we have 3x3 neighborhoods around the central 00234 * point [s], and del2half = 4 prior neighbors that are ordered 00235 * as shown below. The subsequent neighbors s' marked by [-] are 00236 * not stored with s because s is a prior neighbor of each such 00237 * s'. Storage is done this way so that every patch of distances 00238 * (ie, the numbered squares below) is still in the same indexing 00239 * order as the original images. So, for example, if we have all 00240 * 9 distances, a simple reshape(dists, [3 3]) will put them in 00241 * an immediately recognizable format. 00242 * n-> 00243 * m 0 3 - 00244 * | 1 s - 00245 * V 2 - - 00246 * Neighbors are indexed by nu = 0...del2half-1 00247 * This layout must match the layout expected by mrf_segment_wts. 00248 */ 00249 for (np = -delhalf, nu = 0; np <= 0; np++) 00250 for (mp = -delhalf; mp <= delhalf; mp++, nu++) { 00251 if (nu >= del2half) break; /* no more neighbors */ 00252 /* find s' = x',y',z' in unit coordinates */ 00253 x0 = n + np - xcen; 00254 y0 = m + mp - ycen; 00255 xp = ( x0 * Ca0 + y0 * Sa0) * RnInv; /* rotate and scale */ 00256 yp = (-x0 * Sa0 + y0 * Ca0) * RmInv; 00257 zp = xp*xp + yp*yp; /* intermediate result */ 00258 /* off-disc check for s' */ 00259 if (zp > 1) { 00260 d0[nu] = DIST_BLANK; /* set only the s' nbr to off-disk */ 00261 continue; /* i.e., next s' = (m+mp,n+np) */ 00262 } 00263 zp = sqrt(1 - zp); /* this is the real z */ 00264 /* (xp,yp,zp) is now a position on the sphere where s' lives */ 00265 /* find angle theta between s and s' */ 00266 /* note, this approximates the arc length with the chord length */ 00267 theta = sqrt((x-xp)*(x-xp) + (y-yp)*(y-yp) + (z-zp)*(z-zp)); 00268 if (raw) 00269 d0[nu] = theta; /* note, ignore rho */ 00270 else 00271 d0[nu] = exp(-rho*theta); /* usual case */ 00272 if (d0[nu] > dmax) dmax = d0[nu]; /* update the max */ 00273 } 00274 } 00275 /* 00276 * Rescale if that was desired 00277 */ 00278 if (rescale) { 00279 dmax = 1.0/dmax; /* invert so the inner loop is a multiply */ 00280 for (nu = 0; nu < maxx*maxy*del2half; nu++) 00281 if (isnan(d[nu])) 00282 d[nu] = DIST_BLANK; /* replace NaN sentinel with blank value */ 00283 else 00284 d[nu] *= dmax; /* a valid distance: rescale it */ 00285 } 00286 return 1; 00287 } 00288 00289 00290 00291 /* 00292 * Gateway routine 00293 */ 00294 #ifdef StaticP /* undefined under mex */ 00295 StaticP 00296 #endif 00297 void 00298 mexFunction( 00299 int nlhs, 00300 mxArray *plhs[], 00301 int nrhs, 00302 const mxArray *prhs[]) 00303 { 00304 int M, N; // size of disc 00305 int maxx, maxy; // sizes, transposed if needed 00306 int strx, stry; // strides in images along x(n) and y(m) dims 00307 int del; // nbhd full width 00308 int dims[3]; // space for 3 dimension lengths 00309 double rho; // rho value which is used for flags too 00310 double Rm, Rn; // radii 00311 double a0; // major axis rotation angle 00312 int mode_sesw; // sesw = 1, transposed = 0 00313 char *mode, *word; // label for converted mode string 00314 int ok; 00315 char errstr[256]; 00316 00317 /* Hook for introspection (function signature, docstring) */ 00318 if (nrhs < 0) { 00319 plhs[0] = mxt_PackSignature((mxt_Signature) (-nrhs), 00320 NARGIN_MIN, NARGIN_MAX, 00321 NARGOUT_MIN, NARGOUT_MAX, 00322 in_names, in_specs, out_names, docstring); 00323 return; 00324 } 00325 /* 00326 * check args 00327 */ 00328 if ((nrhs < NARGIN_MIN) || (nrhs > NARGIN_MAX)) 00329 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00330 "%s: Expect %d <= input args <= %d", 00331 progname, NARGIN_MIN, NARGIN_MAX), errstr)); 00332 if ((nlhs < NARGOUT_MIN) || (nlhs > NARGOUT_MAX)) 00333 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00334 "%s: Expect %d <= output args <= %d", 00335 progname, NARGOUT_MIN, NARGOUT_MAX), errstr)); 00336 mexargparse(nrhs, prhs, in_names, in_specs, NULL, progname); 00337 00338 /* n, 1x1 or 1x2 */ 00339 M = (int) (mxGetPr(prhs[ARG_n])[0]); 00340 if (mxGetNumberOfElements(prhs[ARG_n]) < 2) 00341 N = M; 00342 else 00343 N = (int) (mxGetPr(prhs[ARG_n])[1]); 00344 /* neighborhood size del */ 00345 del = mxGetScalar(prhs[ARG_del]); 00346 00347 /* 00348 * `mode' input 00349 * (required) 00350 */ 00351 if ((mode = mxArrayToString(prhs[ARG_mode])) == NULL) 00352 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00353 "%s: bad mode (non-string?). " 00354 "Could not convert mode arg to string.", 00355 progname), errstr)); 00356 /* no default values -- user must specify both explicitly */ 00357 mode_sesw = -1; 00358 if ((word = extract_mode(mode, &mode_sesw)) != NULL) 00359 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00360 "%s: bad word <%s> in mode <%.80s>", 00361 progname, word, /* need a fresh copy of the mode */ 00362 mxArrayToString(prhs[ARG_mode])), errstr)); 00363 if (mode_sesw < 0) 00364 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00365 "%s: need sesw/sene in mode <%.80s>", 00366 progname, /* need a fresh copy of the mode */ 00367 mxArrayToString(prhs[ARG_mode])), errstr)); 00368 mxFree(mode); // done with the mode string 00369 00370 /* 00371 * create space for output 00372 */ 00373 dims[0] = (del*del-1)/2; 00374 dims[1] = M; 00375 dims[2] = N; 00376 /* plhs[ARG_dist] = mxCreateDoubleMatrix(M * (del*del-1)/2, N, mxREAL); */ 00377 plhs[ARG_dist] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL); 00378 if (!plhs[ARG_dist]) 00379 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00380 "%s: mxCreate call: failed for dist", 00381 progname), errstr)); 00382 rho = mxGetPr(prhs[ARG_rho])[0]; /* output strength, scaling, type */ 00383 00384 /* 00385 * ellipticity params 00386 */ 00387 // major axis length 00388 Rm = mxGetPr(prhs[ARG_ctr])[2]; 00389 // ellipticity, if any 00390 if (nrhs <= ARG_ell) { 00391 // no ellipticity given: make it a circle 00392 Rn = Rm; // n or x scale same as m or y scale 00393 a0 = 0.0; // rotation angle is arbitrary in this case 00394 } else { 00395 // elliptical 00396 Rn = mxGetPr(prhs[ARG_ell])[0]; // length given explicitly 00397 a0 = mxGetPr(prhs[ARG_ell])[1]*(-1*M_PI/180.0); // deg->radians 00398 } 00399 00400 /* 00401 * Depending on pixel-order mode, set up strides and bounding boxes in 00402 * (x,y) coordinates from their (m,n) settings. 00403 * Note that the (x0,y0) positions, in the geom input, 00404 * correspond to x and to y, so they do not need to be swapped. 00405 */ 00406 if (mode_sesw) { 00407 // x (E-W axis) varies fastest -- x corresponds to matlab m coordinate 00408 strx = 1; stry = M; // x varies fastest, x has M points... 00409 maxx = M; maxy = N; // y has N points 00410 } else { 00411 // y (N-S axis) varies fastest -- x corresponds to matlab n coordinate 00412 strx = M; stry = 1; // y varies fastest, y has M points... 00413 maxx = N; maxy = M; // and x has N points 00414 } 00415 00416 /* 00417 * do the computation 00418 */ 00419 ok = makedist( 00420 mxGetPr(plhs[ARG_dist]), 00421 fabs(rho), // allow for rho < 0, don't care if NaN 00422 isnan(rho), // NaN means output raw chord lengths 00423 (int) (rho < 0), // rho < 0: rescale d so max(*d) = 1 00424 // disc center 00425 mxGetPr(prhs[ARG_ctr])[0]-1.0, // ctr n/x, C origin 00426 mxGetPr(prhs[ARG_ctr])[1]-1.0, // ctr m/y, C origin 00427 // disc radii 00428 Rm, Rn, 00429 // major axis orientation 00430 a0, 00431 // sizes as ints 00432 del, maxx, maxy, strx, stry); 00433 if (!ok) { 00434 mexErrMsgTxt((snprintf(errstr, sizeof(errstr), 00435 "%s: makedist() call: failed", 00436 progname), errstr)); 00437 } 00438 } 00439 00440 00441 00442 /* Hook for generic tail matter */ 00443 #ifdef MEX2C_TAIL_HOOK 00444 #include "mex2c_tail.h" 00445 #endif 00446