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 = float(vnl_math::deg_per_rad);
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 }
00131 else {
00132 vcl_cout << "Can not estimate sensor & texture noise\n";
00133 noiseSigma = 1;
00134 }
00135 }
00136 else {
00137 vcl_cout << "Not enough edge elements to estimate noise\n";
00138 noiseSigma = 1;
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
00164
00165 gevd_float_operators::NonMaximumSuppression(*curvature, *dirx, *diry,
00166 NoiseThreshold(),
00167 contour, direction,
00168 locationx, locationy);
00169 delete curvature; delete dirx; delete diry;
00170 gevd_float_operators::FillFrameX(*contour, 0, FRAME);
00171 gevd_float_operators::FillFrameY(*contour, 0, FRAME);
00172 return true;
00173 }
00174
00175
00176
00177
00178 static int
00179 LeftXorRightEnd(const gevd_bufferxy& contour,
00180 int i, int j,
00181 int dir)
00182 {
00183 int di = DIS[dir], dj = DJS[dir];
00184 bool normalp = (floatPixel(contour, i - di, j - dj) ||
00185 floatPixel(contour, i + di, j + dj));
00186 if (normalp)
00187 return 0;
00188 bool leftp = false;
00189 int ndir = dir - HALFPI;
00190 for (int n = 0; n < 3; ++n) {
00191 int theta = ndir + RDS[n];
00192 if (floatPixel(contour, i+DIS[theta], j+DJS[theta])) {
00193 leftp = true;
00194 break;
00195 }
00196 }
00197 bool rightp = false;
00198 ndir = dir + HALFPI;
00199 for (int n = 0; n < 3; ++n) {
00200 int theta = ndir + RDS[n];
00201 if (floatPixel(contour, i+DIS[theta], j+DJS[theta])) {
00202 rightp = true;
00203 break;
00204 }
00205 }
00206 return (leftp? 0: -HALFPI) + (rightp? 0: HALFPI);
00207 }
00208
00209
00210
00211
00212
00213 static float
00214 BestFoldExtension(const gevd_bufferxy& smooth,
00215 int i, int j,
00216 int ndir,
00217 float threshold,
00218 int& best_i, int& best_j,
00219 int& best_d, float& best_l)
00220 {
00221 float best_s = threshold;
00222 const int direc = ndir + HALFPI;
00223 for (int n = 0; n < 3; n++) {
00224 int ntheta = ndir + RDS[n];
00225 int ni = i + DIS[ntheta];
00226 int nj = j + DJS[ntheta];
00227 float pix = floatPixel(smooth, ni, nj);
00228 for (int d = 0; d < 3; d++) {
00229 int dir = direc + RDS[d];
00230 int di = DIS[dir];
00231 int dj = DJS[dir];
00232 float pix_m = floatPixel(smooth, ni-di, nj-dj);
00233 float pix_p = floatPixel(smooth, ni+di, nj+dj);
00234 float curvature = (float)vcl_fabs(pix_p + pix_m - 2*pix);
00235 float max_s = (dir%HALFPI)? best_s*2: best_s;
00236 if (curvature > max_s) {
00237 int di2 = 2*di;
00238 int dj2 = 2*dj;
00239 if (curvature > vcl_fabs(pix + floatPixel(smooth, ni-di2, nj-dj2)
00240 - 2 * pix_m) &&
00241 curvature > vcl_fabs(pix + floatPixel(smooth, ni+di2, nj+dj2)
00242 - 2 * pix_p)) {
00243 best_i = ni;
00244 best_j = nj;
00245 best_s = (dir%HALFPI)? curvature/2 : curvature;
00246 best_d = dir%FULLPI + TWOPI;
00247 }
00248 }
00249 }
00250 }
00251 if (best_s > threshold) {
00252 float pix = floatPixel(smooth, best_i, best_j);
00253 int di = DIS[best_d], dj = DJS[best_d];
00254 int di2 = 2*di, dj2 = 2*dj;
00255 float s_m = (float)vcl_fabs(pix + floatPixel(smooth, best_i-di2, best_j-dj2)
00256 - 2*floatPixel(smooth, best_i-di, best_j-dj));
00257 float s_p = (float)vcl_fabs(pix + floatPixel(smooth, best_i+di2, best_j+dj2)
00258 - 2*floatPixel(smooth, best_i+di, best_j+dj));
00259 if (best_d%HALFPI) {
00260 s_m /= (float)2.0;
00261 s_p /= (float)2.0;
00262 }
00263 best_l = gevd_float_operators::InterpolateParabola(s_m, best_s, s_p, best_s);
00264 return best_s;
00265 }
00266 else
00267 return 0;
00268 }
00269
00270
00271 int
00272 gevd_fold::RecoverJunctions(const gevd_bufferxy& image,
00273 gevd_bufferxy& contour, gevd_bufferxy& direction,
00274 gevd_bufferxy& locationx, gevd_bufferxy& locationy,
00275 int*& junctionx, int*& junctiony)
00276 {
00277 #if defined(DEBUG)
00278 vul_timer t;
00279 #endif
00280 if (image.GetBitsPixel() != bits_per_float) {
00281 vcl_cerr << "gevd_fold::RecoverJunction requires float image\n";
00282 return false;
00283 }
00284 const int rmax = 1+FRAME;
00285 const int kmax = int(4 * smoothSigma + 0.5) + 2;
00286 const int xmax = image.GetSizeX()-rmax-1;
00287 const int ymax = image.GetSizeY()-rmax-1;
00288 #ifdef DEBUG
00289 vcl_cout << "RecoverJunctions: rmax, kmax, xmax, ymax:" << rmax << ' ' << kmax << ' ' << xmax << ' ' << ymax << '\n';
00290 #endif
00291
00292
00293
00294
00295 vcl_vector<int> ndir;
00296 vcl_vector<int> xloc;
00297 vcl_vector<int> yloc;
00298 int xdir;
00299 for (int y = rmax; y <= ymax; y++)
00300 for (int x = rmax; x <= xmax; x++)
00301 if (floatPixel(contour, x, y) &&
00302 (xdir = LeftXorRightEnd(contour, x, y,
00303 bytePixel(direction, x, y))) != 0)
00304 {
00305 ndir.push_back(xdir);
00306 xloc.push_back(x);
00307 yloc.push_back(y);
00308 }
00309 const int length = ndir.size();
00310
00311
00312
00313
00314 gevd_bufferxy* smooth = NULL;
00315 gevd_float_operators::Gaussian((gevd_bufferxy&)image, smooth, smoothSigma/2);
00316 const bool shortp = true;
00317 const float threshold = NoiseThreshold(shortp);
00318 float curvature, loc;
00319 int njunction = 0;
00320 for (int r = 1; r <= kmax; r++) {
00321 int ntouch = 0, nextension = 0;
00322 for (int i = 0; i < length; i++)
00323 if ((xdir = ndir[i]) != 0 && xdir != TWOPI)
00324 {
00325 int x = xloc[i], y = yloc[i];
00326 int dir = bytePixel(direction, x, y);
00327 curvature = BestFoldExtension(*smooth,
00328 x, y, dir + xdir,
00329 threshold,
00330 x, y, dir, loc);
00331 if (curvature) {
00332 xloc[i] = x, yloc[i] = y;
00333 xdir = LeftXorRightEnd(contour,
00334 x, y, dir);
00335
00336 if (xdir)
00337 {
00338 if (x < rmax || x > xmax ||
00339 y < rmax || y > ymax)
00340 xdir = 0;
00341 else
00342 nextension++;
00343 floatPixel(contour, x, y) = curvature;
00344 }
00345 else
00346 {
00347 ntouch++;
00348 xdir = TWOPI;
00349 }
00350 ndir[i] = xdir;
00351 bytePixel(direction, x, y) = (unsigned char)(dir);
00352 floatPixel(locationx, x, y) = loc*DIS[dir];
00353 floatPixel(locationy, x, y) = loc*DJS[dir];
00354 }
00355 else
00356 ndir[i] = 0;
00357 }
00358
00359
00360 njunction += ntouch;
00361 if (!nextension) break;
00362 }
00363 delete smooth;
00364
00365
00366 if (junctionx) delete [] junctionx;
00367 if (junctiony) delete [] junctiony;
00368 junctionx = new int[njunction];
00369 junctiony = new int[njunction];
00370 for (int i = 0, j = 0; i < length; i++)
00371 if (ndir[i] == TWOPI) {
00372 junctionx[j] = xloc[i];
00373 junctiony[j] = yloc[i];
00374 j++;
00375 }
00376 #if defined(DEBUG)
00377 vcl_cout << "Find " << length << " end points, and "
00378 << njunction << " junctions.\n"
00379 << "Recover " << 100.0*njunction/length
00380 << "% end points as junctions > "
00381 << threshold << ", in " << t.real() << " msecs.\n";
00382 #endif
00383 return njunction;
00384 }
00385
00386
00387 float
00388 gevd_fold::NoiseSigma() const
00389 {
00390 return (noiseSigma <= 0)? 0: noiseSigma;
00391 }
00392
00393
00394 float
00395 gevd_fold::NoiseResponse() const
00396 {
00397 return NoiseResponseToFilter(noiseSigma,
00398 smoothSigma, filterFactor);
00399 }
00400
00401
00402 float
00403 gevd_fold::NoiseThreshold(bool shortp) const
00404 {
00405 float factor = (shortp? junctionFactor : contourFactor);
00406 float smooth = (shortp? smoothSigma/2 : smoothSigma);
00407 return factor * 3 *
00408 NoiseResponseToFilter(noiseSigma, smooth, filterFactor);
00409 }
00410
00411
00412 float
00413 gevd_fold::NoiseResponseToFilter(float noiseSigma,
00414 float smoothSigma,
00415 float filterFactor)
00416 {
00417 return noiseSigma /
00418 (float)vcl_pow((double)smoothSigma, 2.5) *
00419 ((float)vcl_sqrt(0.1875 * vnl_math::two_over_sqrtpi)) *
00420 filterFactor;
00421 }
00422
00423
00424
00425 vcl_ostream& operator<< (vcl_ostream& os, const gevd_fold& st)
00426 {
00427 os << "Fold:\n"
00428 << " smoothSigma " << st.smoothSigma << vcl_endl
00429 << " noiseSigma " << st.noiseSigma << vcl_endl
00430 << " contourFactor " << st.contourFactor << vcl_endl
00431 << " junctionFactor " << st.junctionFactor << vcl_endl
00432 << " filterFactor " << st.filterFactor << vcl_endl;
00433 return os;
00434 }
00435
00436
00437
00438 vcl_ostream& operator<< (vcl_ostream& os, gevd_fold& st)
00439 {
00440 os << "Fold:\n"
00441 << " smoothSigma " << st.smoothSigma << vcl_endl
00442 << " noiseSigma " << st.noiseSigma << vcl_endl
00443 << " contourFactor " << st.contourFactor << vcl_endl
00444 << " junctionFactor " << st.junctionFactor << vcl_endl
00445 << " filterFactor " << st.filterFactor << vcl_endl;
00446 return os;
00447 }