00001
00002 #include "gevd_fold.h"
00003
00004
00005
00006
00007
00008
00009 #include <vcl_vector.h>
00010 #include <vcl_iostream.h>
00011 #include <vnl/vnl_math.h>
00012 #include <gevd/gevd_noise.h>
00013 #include <gevd/gevd_float_operators.h>
00014 #include <gevd/gevd_pixel.h>
00015 #include <gevd/gevd_bufferxy.h>
00016 #ifdef DEBUG
00017 # include <vul/vul_timer.h>
00018 #endif
00019
00020 gevd_bufferxy* gevd_fold::null_bufferxy = 0;
00021
00022 const unsigned char TWOPI = 8, FULLPI = 4, HALFPI = 2;
00023 const int DIS[] = { 1, 1, 0,-1,-1,-1, 0, 1,
00024 1, 1, 0,-1,-1,-1, 0, 1,
00025 1, 1, 0,-1,-1,-1, 0, 1};
00026 const int DJS[] = { 0, 1, 1, 1, 0,-1,-1,-1,
00027 0, 1, 1, 1, 0,-1,-1,-1,
00028 0, 1, 1, 1, 0,-1,-1,-1};
00029 const int RDS[] = {0,-1, 1,-2, 2,-3, 3,-4, 4,-5, 5};
00030
00031
00032 const int FRAME = 4;
00033
00034 gevd_fold::gevd_fold(float smooth_sigma,
00035 float noise_sigma,
00036 float contour_factor, float junction_factor)
00037 : smoothSigma(smooth_sigma), noiseSigma(noise_sigma),
00038 contourFactor(contour_factor), junctionFactor(junction_factor),
00039 filterFactor(6)
00040 {
00041 if (smoothSigma < 0.5)
00042 vcl_cerr << "gevd_fold::gevd_fold -- too small smooth_sigma: "
00043 << smoothSigma << vcl_endl;
00044 if (smoothSigma > 2)
00045 vcl_cerr << "gevd_fold::gevd_fold -- too large smooth_sigma: "
00046 << smoothSigma << vcl_endl;
00047 if (noiseSigma < -1) {
00048 vcl_cerr << "gevd_fold::gevd_fold -- noiseSigma out of range -[0 1]: "
00049 << noiseSigma << ". Reset to -1.\n";
00050 noiseSigma = -1;
00051 }
00052
00053
00054 }
00055
00056
00057 bool
00058 gevd_fold::DetectEdgels(const gevd_bufferxy& image,
00059 gevd_bufferxy*& contour, gevd_bufferxy*& direction,
00060 gevd_bufferxy*& locationx, gevd_bufferxy*& locationy,
00061 bool peaks_only,
00062 bool valleys_only,
00063 bool transfer,
00064 gevd_bufferxy*& mag, gevd_bufferxy*& angle)
00065 {
00066
00067
00068
00069 if (image.GetBitsPixel() != bits_per_float) {
00070 vcl_cerr << "gevd_fold::DetectEdgels requires float image\n";
00071 return false;
00072 }
00073
00074
00075
00076
00077 gevd_bufferxy* smooth = NULL;
00078
00079 filterFactor = gevd_float_operators::Gaussian((gevd_bufferxy&)image,
00080 smooth, smoothSigma);
00081
00082
00083 gevd_bufferxy *curvature = NULL;
00084
00085
00086 gevd_bufferxy *dirx = gevd_float_operators::SimilarBuffer(image);
00087 gevd_bufferxy *diry = gevd_float_operators::SimilarBuffer(image);
00088 filterFactor *= gevd_float_operators::Hessian(*smooth,
00089 curvature, dirx, diry);
00090
00091 for (int j = 0; j < image.GetSizeY(); j++)
00092 for (int i = 0; i < image.GetSizeX(); i++)
00093 if ( (peaks_only && floatPixel(*diry, i, j)<0) ||
00094 (valleys_only && floatPixel(*diry, i, j)>0)
00095 )
00096 floatPixel(*curvature, i, j) = 0;
00097
00098 delete smooth;
00099
00100
00101
00102
00103 if (transfer)
00104 {
00105 mag = gevd_float_operators::SimilarBuffer(image);
00106 angle = gevd_float_operators::SimilarBuffer(image);
00107 const float kdeg = 180.f*float(vnl_math::one_over_pi);
00108 for (int j = 0; j < image.GetSizeY(); j++)
00109 for (int i = 0; i < image.GetSizeX(); i++)
00110 if ((floatPixel(*mag, i, j) = floatPixel(*curvature, i, j)))
00111 floatPixel(*angle, i, j) = kdeg*vcl_atan2(floatPixel(*diry, i, j),
00112 floatPixel(*dirx, i, j));
00113 else
00114 floatPixel(*angle, i, j) = 0;
00115 }
00116
00117
00118
00119 if (noiseSigma <= 0) {
00120 int nedgel = 0;
00121 float* edgels = gevd_noise::EdgelsInCenteredROI(*curvature, *dirx, *diry, nedgel);
00122 if (edgels) {
00123 gevd_noise noise(edgels, nedgel);
00124 delete [] edgels;
00125 float sensorNoise, textureNoise;
00126 if (noise.EstimateSensorTexture(sensorNoise, textureNoise)) {
00127 const float k = -noiseSigma;
00128 noiseSigma = ((1-k)*sensorNoise + k*textureNoise) /
00129 NoiseResponseToFilter(1, smoothSigma, filterFactor);
00130 } else {
00131 vcl_cout << "Can not estimate sensor & texture noise\n";
00132 noiseSigma = 1;
00133 }
00134 } else {
00135 vcl_cout << "Not enough edge elements to estimate noise\n";
00136 noiseSigma = 1;
00137 }
00138
00139 }
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163 gevd_float_operators::NonMaximumSuppression(*curvature, *dirx, *diry,
00164 NoiseThreshold(),
00165 contour, direction,
00166 locationx, locationy);
00167 delete curvature; delete dirx; delete diry;
00168 gevd_float_operators::FillFrameX(*contour, 0, FRAME);
00169 gevd_float_operators::FillFrameY(*contour, 0, FRAME);
00170 return true;
00171 }
00172
00173
00174
00175
00176 static int
00177 LeftXorRightEnd(const gevd_bufferxy& contour,
00178 int i, int j,
00179 int dir)
00180 {
00181 int di = DIS[dir], dj = DJS[dir];
00182 bool normalp = (floatPixel(contour, i - di, j - dj) ||
00183 floatPixel(contour, i + di, j + dj));
00184 if (normalp)
00185 return 0;
00186 bool leftp = false;
00187 int ndir = dir - HALFPI;
00188 for (int n = 0; n < 3; ++n) {
00189 int theta = ndir + RDS[n];
00190 if (floatPixel(contour, i+DIS[theta], j+DJS[theta])) {
00191 leftp = true;
00192 break;
00193 }
00194 }
00195 bool rightp = false;
00196 ndir = dir + HALFPI;
00197 for (int n = 0; n < 3; ++n) {
00198 int theta = ndir + RDS[n];
00199 if (floatPixel(contour, i+DIS[theta], j+DJS[theta])) {
00200 rightp = true;
00201 break;
00202 }
00203 }
00204 return (leftp? 0: -HALFPI) + (rightp? 0: HALFPI);
00205 }
00206
00207
00208
00209
00210
00211 static float
00212 BestFoldExtension(const gevd_bufferxy& smooth,
00213 int i, int j,
00214 int ndir,
00215 float threshold,
00216 int& best_i, int& best_j,
00217 int& best_d, float& best_l)
00218 {
00219 float best_s = threshold;
00220 const int direc = ndir + HALFPI;
00221 for (int n = 0; n < 3; n++) {
00222 int ntheta = ndir + RDS[n];
00223 int ni = i + DIS[ntheta];
00224 int nj = j + DJS[ntheta];
00225 float pix = floatPixel(smooth, ni, nj);
00226 for (int d = 0; d < 3; d++) {
00227 int dir = direc + RDS[d];
00228 int di = DIS[dir];
00229 int dj = DJS[dir];
00230 float pix_m = floatPixel(smooth, ni-di, nj-dj);
00231 float pix_p = floatPixel(smooth, ni+di, nj+dj);
00232 float curvature = (float)vcl_fabs(pix_p + pix_m - 2*pix);
00233 float max_s = (dir%HALFPI)? best_s*2: best_s;
00234 if (curvature > max_s) {
00235 int di2 = 2*di;
00236 int dj2 = 2*dj;
00237 if (curvature > vcl_fabs(pix + floatPixel(smooth, ni-di2, nj-dj2)
00238 - 2 * pix_m) &&
00239 curvature > vcl_fabs(pix + floatPixel(smooth, ni+di2, nj+dj2)
00240 - 2 * pix_p)) {
00241 best_i = ni;
00242 best_j = nj;
00243 best_s = (dir%HALFPI)? curvature/2 : curvature;
00244 best_d = dir%FULLPI + TWOPI;
00245 }
00246 }
00247 }
00248 }
00249 if (best_s > threshold) {
00250 float pix = floatPixel(smooth, best_i, best_j);
00251 int di = DIS[best_d], dj = DJS[best_d];
00252 int di2 = 2*di, dj2 = 2*dj;
00253 float s_m = (float)vcl_fabs(pix + floatPixel(smooth, best_i-di2, best_j-dj2)
00254 - 2*floatPixel(smooth, best_i-di, best_j-dj));
00255 float s_p = (float)vcl_fabs(pix + floatPixel(smooth, best_i+di2, best_j+dj2)
00256 - 2*floatPixel(smooth, best_i+di, best_j+dj));
00257 if (best_d%HALFPI) {
00258 s_m /= (float)2.0;
00259 s_p /= (float)2.0;
00260 }
00261 best_l = gevd_float_operators::InterpolateParabola(s_m, best_s, s_p, best_s);
00262 return best_s;
00263 } else
00264 return 0;
00265 }
00266
00267
00268 int
00269 gevd_fold::RecoverJunctions(const gevd_bufferxy& image,
00270 gevd_bufferxy& contour, gevd_bufferxy& direction,
00271 gevd_bufferxy& locationx, gevd_bufferxy& locationy,
00272 int*& junctionx, int*& junctiony)
00273 {
00274 #if defined(DEBUG)
00275 vul_timer t;
00276 #endif
00277 if (image.GetBitsPixel() != bits_per_float) {
00278 vcl_cerr << "gevd_fold::RecoverJunction requires float image\n";
00279 return false;
00280 }
00281 const int rmax = 1+FRAME;
00282 const int kmax = int(4 * smoothSigma + 0.5) + 2;
00283 const int xmax = image.GetSizeX()-rmax-1;
00284 const int ymax = image.GetSizeY()-rmax-1;
00285 #ifdef DEBUG
00286 vcl_cout << "RecoverJunctions: rmax, kmax, xmax, ymax:" << rmax << ' ' << kmax << ' ' << xmax << ' ' << ymax << '\n';
00287 #endif
00288
00289
00290
00291
00292 vcl_vector<int> ndir;
00293 vcl_vector<int> xloc;
00294 vcl_vector<int> yloc;
00295 int xdir;
00296 for (int y = rmax; y <= ymax; y++)
00297 for (int x = rmax; x <= xmax; x++)
00298 if (floatPixel(contour, x, y) &&
00299 (xdir = LeftXorRightEnd(contour, x, y,
00300 bytePixel(direction, x, y))) != 0)
00301 {
00302 ndir.push_back(xdir);
00303 xloc.push_back(x);
00304 yloc.push_back(y);
00305 }
00306 const int length = ndir.size();
00307
00308
00309
00310
00311 gevd_bufferxy* smooth = NULL;
00312 gevd_float_operators::Gaussian((gevd_bufferxy&)image, smooth, smoothSigma/2);
00313 const bool shortp = true;
00314 const float threshold = NoiseThreshold(shortp);
00315 float curvature, loc;
00316 int njunction = 0;
00317 for (int r = 1; r <= kmax; r++) {
00318 int ntouch = 0, nextension = 0;
00319 for (int i = 0; i < length; i++)
00320 if ((xdir = ndir[i]) != 0 && xdir != TWOPI)
00321 {
00322 int x = xloc[i], y = yloc[i];
00323 int dir = bytePixel(direction, x, y);
00324 curvature = BestFoldExtension(*smooth,
00325 x, y, dir + xdir,
00326 threshold,
00327 x, y, dir, loc);
00328 if (curvature) {
00329 xloc[i] = x, yloc[i] = y;
00330 xdir = LeftXorRightEnd(contour,
00331 x, y, dir);
00332
00333 if (xdir)
00334 {
00335 if (x < rmax || x > xmax ||
00336 y < rmax || y > ymax)
00337 xdir = 0;
00338 else
00339 nextension++;
00340 floatPixel(contour, x, y) = curvature;
00341 }
00342 else
00343 {
00344 ntouch++;
00345 xdir = TWOPI;
00346 }
00347 ndir[i] = xdir;
00348 bytePixel(direction, x, y) = (unsigned char)(dir);
00349 floatPixel(locationx, x, y) = loc*DIS[dir];
00350 floatPixel(locationy, x, y) = loc*DJS[dir];
00351 } else
00352 ndir[i] = 0;
00353 }
00354
00355
00356 njunction += ntouch;
00357 if (!nextension) break;
00358 }
00359 delete smooth;
00360
00361
00362 if (junctionx) delete [] junctionx;
00363 if (junctiony) delete [] junctiony;
00364 junctionx = new int[njunction];
00365 junctiony = new int[njunction];
00366 for (int i = 0, j = 0; i < length; i++)
00367 if (ndir[i] == TWOPI) {
00368 junctionx[j] = xloc[i];
00369 junctiony[j] = yloc[i];
00370 j++;
00371 }
00372 #if defined(DEBUG)
00373 vcl_cout << "Find " << length << " end points, and "
00374 << njunction << " junctions.\n"
00375 << "Recover " << 100.0*njunction/length
00376 << "% end points as junctions > "
00377 << threshold << ", in " << t.real() << " msecs.\n";
00378 #endif
00379 return njunction;
00380 }
00381
00382
00383 float
00384 gevd_fold::NoiseSigma() const
00385 {
00386 return (noiseSigma <= 0)? 0: noiseSigma;
00387 }
00388
00389
00390 float
00391 gevd_fold::NoiseResponse() const
00392 {
00393 return NoiseResponseToFilter(noiseSigma,
00394 smoothSigma, filterFactor);
00395 }
00396
00397
00398 float
00399 gevd_fold::NoiseThreshold(bool shortp) const
00400 {
00401 float factor = (shortp? junctionFactor : contourFactor);
00402 float smooth = (shortp? smoothSigma/2 : smoothSigma);
00403 return factor * 3 *
00404 NoiseResponseToFilter(noiseSigma, smooth, filterFactor);
00405 }
00406
00407
00408 float
00409 gevd_fold::NoiseResponseToFilter(float noiseSigma,
00410 float smoothSigma,
00411 float filterFactor)
00412 {
00413 return noiseSigma /
00414 (float)vcl_pow((double)smoothSigma, 2.5) *
00415 ((float)vcl_sqrt(0.1875 * vnl_math::two_over_sqrtpi)) *
00416 filterFactor;
00417 }
00418
00419
00420
00421 vcl_ostream& operator<< (vcl_ostream& os, const gevd_fold& st)
00422 {
00423 os << "Fold:\n"
00424 << " smoothSigma " << st.smoothSigma << vcl_endl
00425 << " noiseSigma " << st.noiseSigma << vcl_endl
00426 << " contourFactor " << st.contourFactor << vcl_endl
00427 << " junctionFactor " << st.junctionFactor << vcl_endl
00428 << " filterFactor " << st.filterFactor << vcl_endl;
00429 return os;
00430 }
00431
00432
00433
00434 vcl_ostream& operator<< (vcl_ostream& os, gevd_fold& st)
00435 {
00436 os << "Fold:\n"
00437 << " smoothSigma " << st.smoothSigma << vcl_endl
00438 << " noiseSigma " << st.noiseSigma << vcl_endl
00439 << " contourFactor " << st.contourFactor << vcl_endl
00440 << " junctionFactor " << st.junctionFactor << vcl_endl
00441 << " filterFactor " << st.filterFactor << vcl_endl;
00442 return os;
00443 }