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> /// Thresholds the provided image. The <paramref name="lessThan"/> parameter will determine whether the pixels that are less than the /// threshold are set to true and the others "false" or vice versa. The thresholding will be performed on the provided channel. /// channel. /// </summary> /// <typeparam name="T">The underlying type of the input image. Must be of interface IComparable.</typeparam> /// <param name="image">The input image</param> /// <param name="threshold">The threshold to use</param> /// <param name="lessThan">Whether pixels less than the threshold are set to "true" and all others to "false", or vice versa</param> /// <param name="channel">The channel on which to perform thresholding</param> /// <returns>The thresholded image</returns> public static BinaryImage Threshold <T>(this IMultichannelImage <T> image, T threshold, bool lessThan, int channel) where T : IComparable <T> { T[, ,] data = image.RawArray; int rows = image.Rows; int columns = image.Columns; BinaryImage resultImage = new BinaryImage(rows, columns); bool[, ,] result = resultImage.RawArray; for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { result[r, c, 0] = data[r, c, channel].CompareTo(threshold) <= 0 ? lessThan : !lessThan; } } return(resultImage); }
/// <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); }
/// <summary> /// Performs connected components analysis. /// </summary> /// <param name="image">The image to analyze</param> /// <returns>The connected components image</returns> public static unsafe LabelImage Compute(BinaryImage image) { _current = 0; int r, c; short currentLabel, currentSet; short label0, label1, label2, label3, set0, set1; short tl, t, tr, l; int rows = image.Rows; int columns = image.Columns; int count, labelCount; short[] equiv = new short[rows * columns]; LabelImage labels = new LabelImage(rows, columns); fixed(bool *imageBuf = image.RawArray) { fixed(short *labelsBuf = labels.RawArray, equivBuf = equiv) { bool * imagePtr = imageBuf + columns + 1; short *tlPtr = labelsBuf; short *tPtr = tlPtr + 1; short *trPtr = tPtr + 1; short *lPtr = tlPtr + columns; short *labelsPtr = lPtr + 1; short *equivPtr = equivBuf; short *set0Ptr, set1Ptr; label0 = label1 = label2 = label3 = 0; currentLabel = 1; r = rows - 2; while (r-- != 0) //for (r = 1; r < rows - 1; r++) { c = columns - 2; // we are already at c+1 while (c-- != 0) //for (c = 1; c < columns - 1; c++) { if (*imagePtr) { tl = *tlPtr; t = *tPtr; tr = *trPtr; l = *lPtr; labelCount = 0; if (tl != 0) { label0 = tl; labelCount++; } if (t != 0) { if (labelCount == 0) { labelCount++; label0 = t; } else if (labelCount == 1 && t != label0) { labelCount++; label1 = t; } } if (tr != 0) { if (labelCount == 0) { labelCount++; label0 = tr; } else if (labelCount == 1 && tr != label0) { labelCount++; label1 = tr; } else if (labelCount == 2 && tr != label0 && tr != label1) { labelCount++; label2 = tr; } } if (l != 0) { if (labelCount == 0) { labelCount++; label0 = l; } else if (labelCount == 1 && l != label0) { labelCount++; label1 = l; } else if (labelCount == 2 && l != label0 && l != label1) { labelCount++; label2 = l; } else if (labelCount == 3 && l != label0 && l != label1 && l != label2) { labelCount++; label3 = l; } } if (labelCount == 0) { *labelsPtr = currentLabel++; } else if (labelCount == 1) { *labelsPtr = label0; } else if (labelCount == 2) { *labelsPtr = label0; list_add(new LabelPair { Label1 = label0, Label2 = label1 }); } else if (labelCount == 3) { *labelsPtr = label0; list_add(new LabelPair { Label1 = label0, Label2 = label1 }); list_add(new LabelPair { Label1 = label0, Label2 = label2 }); list_add(new LabelPair { Label1 = label1, Label2 = label2 }); } else if (labelCount == 4) { *labelsPtr = label0; list_add(new LabelPair { Label1 = label0, Label2 = label1 }); list_add(new LabelPair { Label1 = label0, Label2 = label2 }); list_add(new LabelPair { Label1 = label0, Label2 = label3 }); list_add(new LabelPair { Label1 = label1, Label2 = label2 }); list_add(new LabelPair { Label1 = label1, Label2 = label3 }); list_add(new LabelPair { Label1 = label2, Label2 = label3 }); } } imagePtr++; tlPtr++; tPtr++; trPtr++; lPtr++; labelsPtr++; } // get to c+1 imagePtr += 2; tlPtr += 2; tPtr += 2; trPtr += 2; lPtr += 2; labelsPtr += 2; } // resolve equivalencies count = currentLabel; while (count-- != 0) { *equivPtr++ = 0; } currentSet = 1; fixed(LabelPair *nodeBuf = _nodes) { LabelPair *nodePtr = nodeBuf; while (_current-- != 0) { LabelPair current = *nodePtr++; set0Ptr = equivBuf + current.Label1; set1Ptr = equivBuf + current.Label2; bool contain1 = *set0Ptr != 0; bool contain2 = *set1Ptr != 0; if (contain1 && contain2) { set0 = *set0Ptr; set1 = *set1Ptr; if (set0 != set1) { equivPtr = equivBuf; count = currentLabel; while (count-- != 0) { if (*equivPtr == set1) { *equivPtr = set0; } equivPtr++; } } } else if (contain1) { *set1Ptr = *set0Ptr; } else if (contain2) { *set0Ptr = *set1Ptr; } else { *set0Ptr = currentSet; *set1Ptr = currentSet; currentSet++; } } } labelsPtr = labelsBuf; count = rows * columns; while (count-- != 0) { currentLabel = *labelsPtr; if (currentLabel == 0) { labelsPtr++; continue; } equivPtr = equivBuf + currentLabel; if (*equivPtr == 0) { *equivPtr = currentSet++; } *labelsPtr++ = *equivPtr; } } } return(labels); }