diff options
Diffstat (limited to 'src/process/im_distance.cpp')
-rw-r--r-- | src/process/im_distance.cpp | 512 |
1 files changed, 512 insertions, 0 deletions
diff --git a/src/process/im_distance.cpp b/src/process/im_distance.cpp new file mode 100644 index 0000000..019356d --- /dev/null +++ b/src/process/im_distance.cpp @@ -0,0 +1,512 @@ +/** \file + * \brief Distance Transform + * + * See Copyright Notice in im_lib.h + * $Id: im_distance.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $ + */ + +#include <im.h> +#include <im_util.h> + +#include "im_process_glo.h" + +#include <stdlib.h> +#include <memory.h> +#include <math.h> + +const float DT_ONE = 1.0f; // 1x0 +const float DT_SQRT2 = 1.414213562373f; // 1x1 +const float DT_SQRT5 = 2.2360679775f; // 2x1 +const float DT_SQRT10 = 3.1622776601684f; // 3x1 +const float DT_SQRT13 = 3.605551275464f; // 3x2 +const float DT_SQRT17 = 4.12310562562f; // 4x1 +const float DT_SQRT25 = 5.0f; // 4x3 + +static inline void setValue(int r, int r1, int r2, int r3, int r4, float *image_data, int f) +{ + float v; + float minv = image_data[r]; // (x,y) + + if (f) + v = image_data[r - 1] + DT_ONE; // (x-1,y) + else + v = image_data[r + 1] + DT_ONE; // (x+1,y) + if (v < minv) minv = v; + + v = image_data[r1] + DT_ONE; // (x,y-1) (x,y+1) + if (v < minv) minv = v; + + if (minv < DT_SQRT2) + goto min_attrib; + + v = image_data[r1 - 1] + DT_SQRT2; // (x-1,y-1) (x-1,y+1) + if (v < minv) minv = v; + + v = image_data[r1 + 1] + DT_SQRT2; // (x+1,y-1) (x+1,y+1) + if (v < minv) minv = v; + + if (minv < DT_SQRT5) + goto min_attrib; + + v = image_data[r1 + 2] + DT_SQRT5; // (x+2,y-1) (x+2,y+1) + if (v < minv) minv = v; + + v = image_data[r1 - 2] + DT_SQRT5; // (x-2,y-1) (x-2,y+1) + if (v < minv) minv = v; + + v = image_data[r2 - 1] + DT_SQRT5; // (x-1,y-2) (x-1,y+2) + if (v < minv) minv = v; + + v = image_data[r2 + 1] + DT_SQRT5; // (x+1,y-2) (x+1,y+2) + if (v < minv) minv = v; + + if (minv < DT_SQRT10) + goto min_attrib; + + v = image_data[r1 + 3] + DT_SQRT10; // (x+3,y-1) (x+3,y+1) + if (v < minv) minv = v; + + v = image_data[r1 - 3] + DT_SQRT10; // (x-3,y-1) (x-3,y+1) + if (v < minv) minv = v; + + v = image_data[r3 - 1] + DT_SQRT10; // (x-1,y-3) (x-1,y+3) + if (v < minv) minv = v; + + v = image_data[r3 + 1] + DT_SQRT10; // (x+1,y-3) (x+1,y+3) + if (v < minv) minv = v; + + if (minv < DT_SQRT13) + goto min_attrib; + + v = image_data[r2 - 3] + DT_SQRT13; // (x-3,y-2) (x-3,y+2) + if (v < minv) minv = v; + + v = image_data[r2 + 3] + DT_SQRT13; // (x+3,y-2) (x+3,y+2) + if (v < minv) minv = v; + + v = image_data[r3 + 2] + DT_SQRT13; // (x+2,y-3) (x+2,y+3) + if (v < minv) minv = v; + + v = image_data[r3 - 2] + DT_SQRT13; // (x-2,y-3) (x-2,y+3) + if (v < minv) minv = v; + + if (minv < DT_SQRT17) + goto min_attrib; + + v = image_data[r1 + 4] + DT_SQRT17; // (x+4,y-1) (x+4,y+1) + if (v < minv) minv = v; + + v = image_data[r1 - 4] + DT_SQRT17; // (x-4,y-1) (x-4,y+1) + if (v < minv) minv = v; + + v = image_data[r4 - 1] + DT_SQRT17; // (x-1,y-4) (x-1,y+4) + if (v < minv) minv = v; + + v = image_data[r4 + 1] + DT_SQRT17; // (x+1,y-4) (x+1,y+4) + if (v < minv) minv = v; + + if (minv < DT_SQRT25) + goto min_attrib; + + v = image_data[r3 - 4] + DT_SQRT25; // (x-4,y-3) (x-4,y+3) + if (v < minv) minv = v; + + v = image_data[r3 + 4] + DT_SQRT25; // (x+4,y-3) (x+4,y+3) + if (v < minv) minv = v; + + v = image_data[r4 + 3] + DT_SQRT25; // (x+3,y-4) (x+3,y+4) + if (v < minv) minv = v; + + v = image_data[r4 - 3] + DT_SQRT25; // (x-3,y-4) (x-3,y+4) + if (v < minv) minv = v; + +min_attrib: + image_data[r] = minv; +} + +static inline void setValueForwardEdge(int r, int r1, int r2, int width, int x, int y, float *image_data) +{ + float v; + float minv = image_data[r]; // (x,y) + + if (y > 0) + { + v = image_data[r1] + DT_ONE; // (x,y-1) + if (v < minv) minv = v; + } + + if (x > 0) + { + v = image_data[r - 1] + DT_ONE; // (x-1,y) + if (v < minv) minv = v; + } + + if (x > 0 && y > 0) + { + v = image_data[r1 - 1] + DT_SQRT2; // (x-1,y-1) + if (v < minv) minv = v; + } + + if (x < width-2 && y > 0) + { + v = image_data[r1 + 1] + DT_SQRT2; // (x+1,y-1) + if (v < minv) minv = v; + } + + if (x > 0 && y > 1) + { + v = image_data[r2 - 1] + DT_SQRT5; // (x-1,y-2) + if (v < minv) minv = v; + } + + if (x < width-2 && y > 1) + { + v = image_data[r2 + 1] + DT_SQRT5; // (x+1,y-2) + if (v < minv) minv = v; + } + + if (x < width-3 && y > 0) + { + v = image_data[r1 + 2] + DT_SQRT5; // (x+2,y-1) + if (v < minv) minv = v; + } + + if (x > 1 && y > 0) + { + v = image_data[r1 - 2] + DT_SQRT5; // (x-2,y-1) + if (v < minv) minv = v; + } + + image_data[r] = minv; +} + +static inline void setValueBackwardEdge(int r, int r1, int r2, int width, int height, int x, int y, float *image_data) +{ + float v; + float minv = image_data[r]; // (x,y) + + if (x < width-2) + { + v = image_data[r + 1] + DT_ONE; // (x+1,y) + if (v < minv) minv = v; + } + + if (y < height-2) + { + v = image_data[r1] + DT_ONE; // (x,y+1) + if (v < minv) minv = v; + } + + if (y < height-2 && x > 0) + { + v = image_data[r1 - 1] + DT_SQRT2; // (x-1,y+1) + if (v < minv) minv = v; + } + + if (y < height-2 && x < width-2) + { + v = image_data[r1 + 1] + DT_SQRT2; // (x+1,y+1) + if (v < minv) minv = v; + } + + if (y < height-2 && x < width-3) + { + v = image_data[r1 + 2] + DT_SQRT5; // (x+2,y+1) + if (v < minv) minv = v; + } + + if (y < height-3 && x < width-2) + { + v = image_data[r2 + 1] + DT_SQRT5; // (x+1,y+2) + if (v < minv) minv = v; + } + + if (y < height-3 && x > 0) + { + v = image_data[r2 - 1] + DT_SQRT5; // (x-1,y+2) + if (v < minv) minv = v; + } + + if (y < height-2 && x > 1) + { + v = image_data[r1 - 2] + DT_SQRT5; // (x-2,y+1) + if (v < minv) minv = v; + } + + image_data[r] = minv; +} + +void imProcessDistanceTransform(const imImage* src_image, imImage* dst_image) +{ + int i, x, y, + offset, offset1, offset2, offset3, offset4, + width = src_image->width, + height = src_image->height; + + imbyte* src_data = (imbyte*)src_image->data[0]; + float* dst_data = (float*)dst_image->data[0]; + + float max_dist = (float)sqrt(double(width*width + height*height)); + + for (i = 0; i < src_image->count; i++) + { + // if pixel is background, then distance is zero. + if (src_data[i]) + dst_data[i] = max_dist; + } + + /* down->top, left->right */ + for (y = 0; y < height; y++) + { + offset = y * width; + offset1 = offset - width; + offset2 = offset - 2*width; + offset3 = offset - 3*width; + offset4 = offset - 4*width; + + for (x = 0; x < width; x++) + { + if (src_data[offset]) + { + if (x < 4 || x > width-5 || y < 4 || y > height-5) + setValueForwardEdge(offset, offset1, offset2, width, x, y, dst_data); + else + setValue(offset, offset1, offset2, offset3, offset4, dst_data, 1); + } + + offset++; + offset1++; + offset2++; + offset3++; + offset4++; + } + } + + /* top->down, right->left */ + for (y = height-1; y >= 0; y--) + { + offset = y * width + width-1; + offset1 = offset + width; + offset2 = offset + 2*width; + offset3 = offset + 3*width; + offset4 = offset + 4*width; + + for (x = width-1; x >= 0; x--) + { + if (src_data[offset]) + { + if (x < 4 || x > width-5 || y < 4 || y > height-5) + setValueBackwardEdge(offset, offset1, offset2, width, height, x, y, dst_data); + else + setValue(offset, offset1, offset2, offset3, offset4, dst_data, 0); + } + + offset--; + offset1--; + offset2--; + offset3--; + offset4--; + } + } +} + +static void iFillValue(imbyte* img_data, int x, int y, int width, int value) +{ + int r = y * width + x; + int r1a = r - width; + int r1b = r + width; + int v; + + int old_value = img_data[r]; + img_data[r] = (imbyte)value; + + v = img_data[r1a]; // (x,y-1) + if (v == old_value) + iFillValue(img_data, x, y-1, width, value); + + v = img_data[r - 1]; // (x-1,y) + if (v == old_value) + iFillValue(img_data, x-1, y, width, value); + + v = img_data[r1a - 1]; // (x-1,y-1) + if (v == old_value) + iFillValue(img_data, x-1, y-1, width, value); + + v = img_data[r1a + 1]; // (x+1,y-1) + if (v == old_value) + iFillValue(img_data, x+1, y-1, width, value); + + v = img_data[r + 1]; // (x+1,y) + if (v == old_value) + iFillValue(img_data, x+1, y, width, value); + + v = img_data[r1b]; // (x,y+1) + if (v == old_value) + iFillValue(img_data, x, y+1, width, value); + + v = img_data[r1b - 1]; // (x-1,y+1) + if (v == old_value) + iFillValue(img_data, x-1, y+1, width, value); + + v = img_data[r1b + 1]; // (x+1,y+1) + if (v == old_value) + iFillValue(img_data, x+1, y+1, width, value); +} + +static inline int iCheckFalseMaximum(int r, int r2a, int r2b, int width, float *src_data) +{ + /* we are ignoring valeys of 1 pixel. */ + /* this is not 100% fail proof */ + float v; + float maxv = src_data[r]; // (x,y) + int r1a = r - width; + int r1b = r + width; + + v = src_data[r2a - 1]; // (x-1,y-2) + if (v > maxv) return 1; + + v = src_data[r2a]; // (x,y-2) + if (v > maxv) return 1; + + v = src_data[r2a + 1]; // (x+1,y-2) + if (v > maxv) return 1; + + v = src_data[r2b - 1]; // (x-1,y+2) + if (v > maxv) return 1; + + v = src_data[r2b]; // (x,y+2) + if (v > maxv) return 1; + + v = src_data[r2b + 1]; // (x+1,y+2) + if (v > maxv) return 1; + + + v = src_data[r2b - 2]; // (x-2,y+2) + if (v > maxv) return 1; + + v = src_data[r1b - 2]; // (x-2,y+1) + if (v > maxv) return 1; + + v = src_data[r - 2]; // (x-2,y) + if (v > maxv) return 1; + + v = src_data[r1a - 2]; // (x-2,y-1) + if (v > maxv) return 1; + + v = src_data[r2a - 2]; // (x-2,y-2) + if (v > maxv) return 1; + + + v = src_data[r2a + 2]; // (x+2,y-2) + if (v > maxv) return 1; + + v = src_data[r1a + 2]; // (x+2,y-1) + if (v > maxv) return 1; + + v = src_data[r + 2]; // (x+2,y) + if (v > maxv) return 1; + + v = src_data[r1b + 2]; // (x+2,y+1) + if (v > maxv) return 1; + + v = src_data[r2b + 2]; // (x+2,y+2) + if (v > maxv) return 1; + + return 0; +} + +static inline void iCheckMaximum(int r, int r1a, int r1b, float *src_data, imbyte* dst_data) +{ + int unique = 1; + float v; + float maxv = src_data[r]; // (x,y) + + v = src_data[r1a]; // (x,y-1) + if (v >= maxv) { maxv = v; unique = 0; } + + v = src_data[r - 1]; // (x-1,y) + if (v >= maxv) { maxv = v; unique = 0; } + + v = src_data[r1a - 1]; // (x-1,y-1) + if (v >= maxv) { maxv = v; unique = 0; } + + v = src_data[r1a + 1]; // (x+1,y-1) + if (v >= maxv) { maxv = v; unique = 0; } + + v = src_data[r + 1]; // (x+1,y) + if (v >= maxv) { maxv = v; unique = 0; } + + v = src_data[r1b]; // (x,y+1) + if (v >= maxv) { maxv = v; unique = 0; } + + v = src_data[r1b - 1]; // (x-1,y+1) + if (v >= maxv) { maxv = v; unique = 0; } + + v = src_data[r1b + 1]; // (x+1,y+1) + if (v >= maxv) { maxv = v; unique = 0; } + + if (src_data[r] < maxv) // not a maximum + dst_data[r] = 0; + else + { + if (unique) // unique maximum + dst_data[r] = 1; + else // can be maximum + dst_data[r] = 2; + } +} + +void imProcessRegionalMaximum(const imImage* src_image, imImage* dst_image) +{ + int i, x, y, offset, offsetA, offsetB, + width = src_image->width, + height = src_image->height; + + float* src_data = (float*)src_image->data[0]; + imbyte* dst_data = (imbyte*)dst_image->data[0]; + + for (y = 1; y < height-1; y++) + { + offset = y * width + 1; + offsetA = offset - width; + offsetB = offset + width; + + for (x = 1; x < width-1; x++) + { + if (src_data[offset]) + iCheckMaximum(offset, offsetA, offsetB, src_data, dst_data); + + offset++; + offsetA++; + offsetB++; + } + } + + // remove false maximum + for (y = 2; y < height-2; y++) + { + offset = y * width + 2; + offsetA = offset - 2*width; + offsetB = offset + 2*width; + + for (x = 2; x < width-2; x++) + { + if (dst_data[offset] == 2) + { + if (iCheckFalseMaximum(offset, offsetA, offsetB, width, src_data)) + iFillValue(dst_data, x, y, width, 0); + } + + offset++; + offsetA++; + offsetB++; + } + } + + // update destiny with remaining maximum + for (i = 0; i < src_image->count; i++) + { + if (dst_data[i] == 2) + dst_data[i] = 1; + } +} |