internal static unsafe DistanceTransformImage ComputeNaive(BinaryImage edges, bool computeSquareRoot) { int rows = edges.Rows; int columns = edges.Columns; List <ImageCell> cells = edges.GetCells(); DistanceTransformImage dt = new DistanceTransformImage(); dt.SetDimensions(edges.Rows, edges.Columns, 3); for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { var distances = from index in cells select new { Distance = distance(index.Row, index.Column, r, c), Index = index }; var nearest = distances.OrderBy(o => o.Distance).First(); if (computeSquareRoot) { dt[r, c, 0] = (int)Math.Sqrt(nearest.Distance); } else { dt[r, c, 0] = nearest.Distance; } dt[r, c, 1] = nearest.Index.Row; dt[r, c, 2] = nearest.Index.Column; } } return(dt); }
/// <summary> /// Computes the distance transform using an efficient Euclidean distance transform. If <paramref name="computeSquareRoot"/> /// is not set, the squared distance will be stored. /// </summary> /// <param name="edges">Edge image</param> /// <param name="computeSquareRoot">Whether to compute the actual distance from the squared distance</param> /// <returns>Distance transform</returns> public static unsafe DistanceTransformImage Compute(BinaryImage edges, bool computeSquareRoot) { int i, j, k; int r, c; int I_1, I_2, r_1, r_2, dr, intersection; int I2new, I2val, I2column, dtVal; int rows = edges.Rows; int columns = edges.Columns; int channels = 3; int stride = columns * channels; Curve[] curves = new Curve[256]; Curve * bottomPtr, curve0Ptr, curve1Ptr; int curvesCount = 0; int[, ,] I2 = new int[rows, columns, channels]; DistanceTransformImage dt = new DistanceTransformImage(); dt.SetDimensions(rows, columns, channels); initLookup(Math.Max(rows, columns)); fixed(bool *edgesBuf = edges.RawArray) { fixed(int *I2Buf = I2, squareBuf = _squareLookup, dtBuf = dt.RawArray) { fixed(Curve *curvesBuf = curves) { bool *edgesPtr = edgesBuf; int * I2Ptr = I2Buf; int * squarePtr = squareBuf; int lastEdge, width; bool *edgesScan; int * I2RevPtr, I2LagPtr, dtPtr, dtScan, I2Scan; for (r = 0, i = rows; i != 0; i--, r++) { lastEdge = INFINITY; if (*edgesPtr) { I2Ptr[0] = 0; I2Ptr[1] = r; I2Ptr[2] = 0; lastEdge = 0; squarePtr = squareBuf; } else { *I2Ptr = INFINITY; } I2LagPtr = I2Ptr; edgesPtr++; I2Ptr += channels; for (j = columns - 1, c = 1; j != 0; j--, c++, edgesPtr++, I2LagPtr += channels, I2Ptr += channels) { if (*edgesPtr) { if (lastEdge == INFINITY) { for (k = c + 1, I2RevPtr = I2Ptr, squarePtr = squareBuf; k != 0; k--, I2RevPtr -= channels, squarePtr++) { I2RevPtr[0] = *squarePtr; I2RevPtr[1] = r; I2RevPtr[2] = c; } } else { width = c - lastEdge; width = width >> 1; for (k = width + 1, I2RevPtr = I2Ptr, squarePtr = squareBuf; k != 0; k--, I2RevPtr -= channels, squarePtr++) { I2RevPtr[0] = *squarePtr; I2RevPtr[1] = r; I2RevPtr[2] = c; } } lastEdge = c; squarePtr = squareBuf; } else { if (*I2LagPtr == INFINITY) { *I2Ptr = INFINITY; } else { I2Ptr[0] = *(++squarePtr); I2Ptr[1] = r; I2Ptr[2] = lastEdge; } } } } for ( edgesPtr = edgesBuf, dtPtr = dtBuf, I2Ptr = I2Buf, c = 0, i = columns; i != 0; I2Ptr += channels, edgesPtr++, dtPtr += channels, c++, i--) { curvesCount = 0; bottomPtr = null; curve0Ptr = null; curve1Ptr = null; for ( I2Scan = I2Ptr, edgesScan = edgesPtr, dtScan = dtPtr, j = rows, r = 0; j != 0; I2Scan += stride, edgesScan += columns, dtScan += stride, r++, j--) { if (*edgesScan) { curvesCount = 1; bottomPtr = curvesBuf; curve1Ptr = curvesBuf; curve0Ptr = null; bottomPtr->Row = r; bottomPtr->End = rows; bottomPtr->B = 0; bottomPtr->Column = c; dtScan[1] = r; dtScan[2] = c; continue; } I2new = INFINITY; I2val = I2Scan[0]; I2column = I2Scan[2]; if (I2val != INFINITY) { if (curvesCount > 0) { dr = r - bottomPtr->Row; I2new = _squareLookup[dr] + bottomPtr->B; if (I2val < I2new) { curvesCount = 1; bottomPtr = curvesBuf; curve1Ptr = curvesBuf; curve0Ptr = null; bottomPtr->Row = r; bottomPtr->End = rows; bottomPtr->B = I2val; bottomPtr->Column = I2column; dtScan[0] = I2val; dtScan[1] = r; dtScan[2] = I2column; Debug.Assert(dtScan[0] == distance(r, c, dtScan[1], dtScan[2])); continue; } } for (; ;) { if (curvesCount == 0) { curvesCount = 1; bottomPtr = curvesBuf; curve1Ptr = curvesBuf; curve0Ptr = null; bottomPtr->Row = r; bottomPtr->End = rows; bottomPtr->B = I2val; bottomPtr->Column = I2column; break; } if (I2val < curve1Ptr->B) { curvesCount--; if (curvesCount > 0) { curve1Ptr--; if (curvesCount > 1) { curve0Ptr--; } else { curve0Ptr = null; } } else { curve1Ptr = null; bottomPtr = null; curve0Ptr = null; } continue; } I_1 = curve1Ptr->B; I_2 = I2val; r_1 = curve1Ptr->Row; r_2 = r; dr = r_2 - r_1; intersection = r_2 + ((I_2 - I_1 - _squareLookup[dr]) / (dr << 1)); if (intersection >= rows) { *I2Scan = INFINITY; break; } if (curve0Ptr == null || intersection > curve0Ptr->End) { curvesCount++; curve1Ptr->End = intersection; curve0Ptr = curve1Ptr; curve1Ptr++; curve1Ptr->Row = r; curve1Ptr->End = rows; curve1Ptr->B = I_2; curve1Ptr->Column = I2column; break; } curvesCount--; if (curvesCount > 0) { curve1Ptr--; if (curvesCount > 1) { curve0Ptr--; } else { curve0Ptr = null; } } else { curve1Ptr = null; bottomPtr = null; curve0Ptr = null; } } } if (curvesCount == 0) { continue; } if (I2new == INFINITY) { dr = r - bottomPtr->Row; I2new = _squareLookup[dr] + bottomPtr->B; } dtScan[0] = I2new; dtScan[1] = bottomPtr->Row; dtScan[2] = bottomPtr->Column; Debug.Assert(dtScan[0] == distance(r, c, dtScan[1], dtScan[2])); if (I2new < I2val) { *I2Scan = INFINITY; } if (bottomPtr->End == r) { curvesCount--; bottomPtr++; } } //continue; curvesCount = 0; bottomPtr = null; curve0Ptr = null; curve1Ptr = null; I2Scan -= stride; dtScan -= stride; edgesScan -= columns; for ( j = rows, r = rows - 1; j != 0; I2Scan -= stride, dtScan -= stride, edgesScan -= columns, r--, j--) { if (*edgesScan) { curvesCount = 1; bottomPtr = curvesBuf; curve1Ptr = curvesBuf; curve0Ptr = null; bottomPtr->Row = r; bottomPtr->End = -1; bottomPtr->B = 0; bottomPtr->Column = c; continue; } I2new = INFINITY; I2val = I2Scan[0]; I2column = I2Scan[2]; dtVal = *dtScan; if (I2val != INFINITY) { if (curvesCount > 0) { dr = bottomPtr->Row - r; I2new = _squareLookup[dr] + bottomPtr->B; if (dtVal < I2new && dtVal < I2val) { curvesCount = 0; curve1Ptr = null; bottomPtr = null; curve0Ptr = null; continue; } if (I2val < I2new) { curvesCount = 1; bottomPtr = curvesBuf; curve1Ptr = curvesBuf; curve0Ptr = null; bottomPtr->Row = r; bottomPtr->End = -1; bottomPtr->B = I2val; bottomPtr->Column = I2column; dtScan[0] = I2val; dtScan[1] = r; dtScan[2] = I2column; Debug.Assert(dtScan[0] == distance(r, c, dtScan[1], dtScan[2])); continue; } } for (; ;) { if (curvesCount == 0) { curvesCount = 1; bottomPtr = curvesBuf; curve1Ptr = curvesBuf; curve0Ptr = null; bottomPtr->Row = r; bottomPtr->End = -1; bottomPtr->B = I2val; bottomPtr->Column = I2column; break; } if (I2val < curve1Ptr->B) { curvesCount--; if (curvesCount > 0) { curve1Ptr--; if (curvesCount > 1) { curve0Ptr--; } else { curve0Ptr = null; } } else { curve1Ptr = null; bottomPtr = null; curve0Ptr = null; } continue; } I_1 = curve1Ptr->B; I_2 = I2val; r_1 = curve1Ptr->Row; r_2 = r; dr = r_1 - r_2; intersection = r_2 - ((I_2 - I_1 - _squareLookup[dr]) / (dr << 1)); if (intersection < 0) { break; } if (curve0Ptr == null || intersection < curve0Ptr->End) { curvesCount++; curve1Ptr->End = intersection; curve0Ptr = curve1Ptr; curve1Ptr++; curve1Ptr->Row = r; curve1Ptr->End = -1; curve1Ptr->B = I_2; curve1Ptr->Column = I2column; break; } curvesCount--; if (curvesCount > 0) { curve1Ptr--; if (curvesCount > 1) { curve0Ptr--; } else { curve0Ptr = null; } } else { curve1Ptr = null; bottomPtr = null; curve0Ptr = null; } } } if (curvesCount == 0) { continue; } if (I2new == INFINITY) { dr = bottomPtr->Row - r; I2new = _squareLookup[dr] + bottomPtr->B; } if ((I2val == INFINITY && dtVal == 0) || I2new < dtVal) { dtScan[0] = I2new; dtScan[1] = bottomPtr->Row; dtScan[2] = bottomPtr->Column; Debug.Assert(dtScan[0] == distance(r, c, dtScan[1], dtScan[2])); } if (bottomPtr->End == r) { curvesCount--; bottomPtr++; } } } if (computeSquareRoot) { dtPtr = dtBuf; for (r = rows; r != 0; r--) { for (c = columns; c != 0; c--, dtPtr += channels) { *dtPtr = (int)Math.Sqrt(*dtPtr); } } } } } } return(dt); }