double distaa3(double *img, double *gximg, double *gyimg, int w, int c, int xc, int yc, int xi, int yi) { double di, df, dx, dy, gx, gy, a; int closest; closest = c-xc-yc*w; // Index to the edge pixel pointed to from c a = img[closest]; // Grayscale value at the edge pixel gx = gximg[closest]; // X gradient component at the edge pixel gy = gyimg[closest]; // Y gradient component at the edge pixel if(a > 1.0) a = 1.0; if(a < 0.0) a = 0.0; // Clip grayscale values outside the range [0,1] if(a == 0.0) return 1000000.0; // Not an object pixel, return "very far" ("don't know yet") dx = (double)xi; dy = (double)yi; di = sqrt(dx*dx + dy*dy); // Length of integer vector, like a traditional EDT if(di==0) { // Use local gradient only at edges // Estimate based on local gradient only df = edgedf(gx, gy, a); } else { // Estimate gradient based on direction to edge (accurate for large di) df = edgedf(dx, dy, a); } return di + df; // Same metric as edtaa2, except at edges (where di=0) }
// Adjust distance values near edges based on the local edge gradient void postprocess(double *img, double *gximg, double *gyimg, int w, int h, short *distx, short *disty, double *dist) { int i, c, n; double a, gx, gy, dx, dy, df, t, u, v, uvmargin; for(i = 0; i < w*h; i++) { dx = distx[i]; dy = disty[i]; if((dx==0) && (dy==0)) continue; // Don't touch edges, they are OK c = i - disty[i]*w - distx[i]; // Index of closest edge pixel a = img[c]; gx = gximg[c]; gy = gyimg[c]; if(dist[i] < 3.0) { // We can improve things only near edges (3.0 is ad hoc) df = edgedf(gx, gy, a); t = dy*gx - dx*gy; // Offset to orthogonal hit point in edge tangent direction u = -df*gx + t*gy; // (u,v) pixel-local coordinates of orthogonal hit point v = -df*gy - t*gx; // (pixel center is at (0,0), pixel is a unit square) uvmargin = 1.0; // Margin +/- 0.5 is formally correct, but allow some overshoot if ((fabs(u) <= uvmargin) && (fabs(v) <= uvmargin)) { // Use estimated orthogonal distance to edge based on gradient dist[i] = sqrt((dx+u)*(dx+u) + (dy+v)*(dy+v)); } } } }
void edtaa3(double *img, double *gx, double *gy, int w, int h, short *distx, short *disty, double *dist) { int x, y, i, c; int offset_u, offset_ur, offset_r, offset_rd, offset_d, offset_dl, offset_l, offset_lu; double olddist, newdist; int cdistx, cdisty, newdistx, newdisty; int changed; double epsilon = 1e-3; /* Initialize index offsets for the current image width */ offset_u = -w; offset_ur = -w+1; offset_r = 1; offset_rd = w+1; offset_d = w; offset_dl = w-1; offset_l = -1; offset_lu = -w-1; /* Initialize the distance images */ for(i=0; i<w*h; i++) { distx[i] = 0; // At first, all pixels point to disty[i] = 0; // themselves as the closest known. if(img[i] <= 0.0) { dist[i]= 1000000.0; // Big value, means "not set yet" } else if (img[i]<1.0) { dist[i] = edgedf(gx[i], gy[i], img[i]); // Gradient-assisted estimate } else { dist[i]= 0.0; // Inside the object } } /* Perform the transformation */ do { changed = 0; /* Scan rows, except first row */ for(y=1; y<h; y++) { /* move index to leftmost pixel of current row */ i = y*w; /* scan right, propagate distances from above & left */ /* Leftmost pixel is special, has no left neighbors */ olddist = dist[i]; if(olddist > 0) // If non-zero distance or not set yet { c = i + offset_u; // Index of candidate for testing cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx; newdisty = cdisty+1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_ur; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx-1; newdisty = cdisty+1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } i++; /* Middle pixels have all neighbors */ for(x=1; x<w-1; x++, i++) { olddist = dist[i]; if(olddist <= 0) continue; // No need to update further c = i+offset_l; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx+1; newdisty = cdisty; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_lu; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx+1; newdisty = cdisty+1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_u; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx; newdisty = cdisty+1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_ur; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx-1; newdisty = cdisty+1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } /* Rightmost pixel of row is special, has no right neighbors */ olddist = dist[i]; if(olddist > 0) // If not already zero distance { c = i+offset_l; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx+1; newdisty = cdisty; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_lu; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx+1; newdisty = cdisty+1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_u; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx; newdisty = cdisty+1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } /* Move index to second rightmost pixel of current row. */ /* Rightmost pixel is skipped, it has no right neighbor. */ i = y*w + w-2; /* scan left, propagate distance from right */ for(x=w-2; x>=0; x--, i--) { olddist = dist[i]; if(olddist <= 0) continue; // Already zero distance c = i+offset_r; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx-1; newdisty = cdisty; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } } /* Scan rows in reverse order, except last row */ for(y=h-2; y>=0; y--) { /* move index to rightmost pixel of current row */ i = y*w + w-1; /* Scan left, propagate distances from below & right */ /* Rightmost pixel is special, has no right neighbors */ olddist = dist[i]; if(olddist > 0) // If not already zero distance { c = i+offset_d; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx; newdisty = cdisty-1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_dl; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx+1; newdisty = cdisty-1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } i--; /* Middle pixels have all neighbors */ for(x=w-2; x>0; x--, i--) { olddist = dist[i]; if(olddist <= 0) continue; // Already zero distance c = i+offset_r; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx-1; newdisty = cdisty; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_rd; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx-1; newdisty = cdisty-1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_d; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx; newdisty = cdisty-1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_dl; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx+1; newdisty = cdisty-1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } /* Leftmost pixel is special, has no left neighbors */ olddist = dist[i]; if(olddist > 0) // If not already zero distance { c = i+offset_r; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx-1; newdisty = cdisty; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_rd; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx-1; newdisty = cdisty-1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; olddist=newdist; changed = 1; } c = i+offset_d; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx; newdisty = cdisty-1; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } /* Move index to second leftmost pixel of current row. */ /* Leftmost pixel is skipped, it has no left neighbor. */ i = y*w + 1; for(x=1; x<w; x++, i++) { /* scan right, propagate distance from left */ olddist = dist[i]; if(olddist <= 0) continue; // Already zero distance c = i+offset_l; cdistx = distx[c]; cdisty = disty[c]; newdistx = cdistx+1; newdisty = cdisty; newdist = DISTAA(c, cdistx, cdisty, newdistx, newdisty); if(newdist < olddist-epsilon) { distx[i]=newdistx; disty[i]=newdisty; dist[i]=newdist; changed = 1; } } } } while(changed); // Sweep until no more updates are made /* The transformation is completed. */ }