/// <summary> /// Computes a sum of the values in the array starting at (<paramref name="row"/>, <paramref name="column"/>) in <paramref name="channel" /> /// in a rectangle described by the offset and size in <paramref name="rect"/>. /// </summary> /// <param name="handler">The handler used to perform the operation</param> /// <param name="row">Reference row</param> /// <param name="column">Reference column</param> /// <param name="channel">Channel to draw values from</param> /// <param name="rect">Offset and size of the rectangle</param> /// <returns>The sum of all values in the rectangle</returns> public static T ComputeRectangleSum <T>(this IArrayHandler <T> handler, int row, int column, int channel, Rectangle rect) { int startRow = row + rect.Top; int startColumn = column + rect.Left; int rows = rect.Height; int columns = rect.Width; return(handler.ComputeRectangleSum(startRow, startColumn, rows, columns, channel)); }
/// <summary> /// Computes an edge image using the provided image. The lower threshold and higher threshold /// provided are those used for hysteresis, on a scale from 0 to 1. /// </summary> /// <param name="image">The image to use for edge-seeking</param> /// <param name="lowThreshold">The lower threshold for hysteresis, from 0 to 1</param> /// <param name="highThreshold">The higher threshold for hysteresis, from 0 to 1</param> /// <returns></returns> public static BinaryImage Compute( GradientImage image, float lowThreshold, float highThreshold ) { IArrayHandler <byte> nms = NonMaximalSuppression(image); return(Hysteresis(image, nms, lowThreshold, highThreshold)); }
/// <summary> /// Convolves an image with the provided two-dimensional kernel. This is done in the spatial /// domain, and as such is not as efficient as using an Fast Fourier Transform. /// </summary> /// <typeparam name="I">Any image whose pixel values are stored as floats</typeparam> /// <param name="image">Image to convolve</param> /// <param name="kernel">The two-dimensional kernel.</param> /// <returns>The filtered image</returns> public static unsafe I Convolve <I>(IArrayHandler <float> image, float[,] kernel) where I : IArrayHandler <float>, new() { int rows = image.Rows; int columns = image.Columns; int channels = image.Channels; int krows = kernel.GetLength(1); int kcols = kernel.GetLength(0); int kernelCenterX = kcols / 2; int kernelCenterY = krows / 2; int stride = columns * channels; float[, ,] data = new float[rows, columns, channels]; fixed(float *src = image.RawArray, dst = data, knl = kernel) { float *srcPtr = src + kernelCenterY * stride + kernelCenterX * channels; float *srcScanStart = src; float *dstPtr = dst + kernelCenterY * stride + kernelCenterX * channels; for (int r = kernelCenterY; r < rows - kernelCenterY; r++) { for (int c = 0; c < columns; c++, srcPtr += channels, srcScanStart += channels) { float[] sums = new float[channels]; float * srcScan = srcScanStart; float * knlPtr = knl; for (int u = 0; u < krows; u++, srcScan += stride - kcols * channels) { for (int v = 0; v < kcols; v++, knlPtr++) { float mult = *knlPtr; for (int i = 0; i < channels; i++, srcScan++) { sums[i] += mult * *srcScan; } } } for (int i = 0; i < channels; i++) { *dstPtr++ = sums[i]; } } } } I result = new I(); result.SetData(data); return(result); }
/// <summary> /// Computes an integral image in one pass from the source image. /// </summary> /// <param name="input">Source image</param> /// <returns>Integral image</returns> public static unsafe T ComputeInteger <T>(IArrayHandler <int> input) where T : IArrayHandler <int>, new() { int rows = input.Rows; int columns = input.Columns; int channels = input.Channels; int[, ,] ii = new int[rows + 1, columns + 1, channels]; int[, ,] s = new int[rows + 1, columns + 1, channels]; int stride = (columns + 1) * channels; fixed(int *iiScan = ii, sScan = s, src = input.RawArray) { int *srcPtr = src; int *iiPtrM = iiScan + stride; int *iiPtr = iiPtrM + channels; int *sPtrM = sScan + channels; int *sPtr = sPtrM + stride; for (int r = 1; r < rows + 1; r++) { for (int c = 1; c < columns + 1; c++) { for (int i = 0; i < channels; i++) { int val = *srcPtr++; * sPtr = *sPtrM + val; * iiPtr = *iiPtrM + *sPtr; sPtr++; iiPtr++; sPtrM++; iiPtrM++; } } sPtrM += channels; sPtr += channels; iiPtrM += channels; iiPtr += channels; } } T result = new T(); result.SetData(ii); result.IsIntegral = true; return(result); }
/// <summary> /// Fills a handler with the provided value. /// </summary> /// <typeparam name="T">The underlying type of the handler</typeparam> /// <param name="handler">The handler to fill</param> /// <param name="value">The value to fill with</param> public static void Fill <T>(this IArrayHandler <T> handler, T value) { T[, ,] data = handler.RawArray; int rows = handler.Rows; int columns = handler.Columns; int channels = handler.Channels; for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { for (int i = 0; i < channels; i++) { data[r, c, i] = value; } } } }
/// <summary> /// Interpolates the value in-between cells in an array using bi-linear interpolation. /// </summary> /// <param name="handler">The array to use</param> /// <param name="row">The real-valued row</param> /// <param name="column">The real-valued column</param> /// <param name="channel">The image channel</param> /// <returns>An interpolated value</returns> public static float InterploateLinear(this IArrayHandler <float> handler, float row, float column, int channel) { row = row < 0 ? 0 : row; column = column < 0 ? 0 : column; int i0 = (int)row; int j0 = (int)column; int i1 = i0 + 1; i1 = i1 < handler.Rows ? i1 : handler.Rows - 1; int j1 = j0 + 1; j1 = j1 < handler.Columns ? j1 : handler.Columns - 1; float di = row - i0; float dj = column - j0; return(handler[i0, j0, channel] * (1 - di) * (1 - dj) + handler[i0, j1, channel] * (1 - di) * dj + handler[i1, j0, channel] * di * (1 - dj) + handler[i1, j1, channel] * di * dj); }
public float Compute(ImageDataPoint <float> point) { if (point.ImageID != _currentID) { _currentID = point.ImageID; if (point.Image.IsIntegral || DecisionTree <ImageDataPoint <float>, float[]> .IsBuilding) { _integralImage = point.Image; } else { _integralImage = IntegralImage.ComputeFloat <FloatArrayHandler>(point.Image); } } int row = point.Row; int column = point.Column; float sum = _integralImage.ComputeRectangleSum(row, column, _channel, _rect); Debug.Assert(!float.IsNaN(sum), "Rectangle sum is NaN"); return(sum); }
/// <summary> /// Extracts a channel as a Grayscale image. Uses the <see cref="M:IArrayHandler.ExtractChannel"/> method. /// </summary> /// <param name="handler">The image upon which to operate</param> /// <param name="channel">The channel to extract</param> /// <returns>An image representation of the channel</returns> public static unsafe GrayscaleImage ExtractChannelAsImage(this IArrayHandler <float> handler, int channel) { float[,] buffer = handler.ExtractChannel(channel); int rows = handler.Rows; int columns = handler.Columns; float[, ,] data = new float[rows, columns, 1]; fixed(float *channelSrc = buffer, dataSrc = data) { float *channelPtr = channelSrc; float *dataPtr = dataSrc; int count = rows * columns; while (count-- > 0) { *dataPtr++ = *channelPtr++; } } GrayscaleImage gray = new GrayscaleImage(); gray.SetData(data); return(gray); }
/// <summary> /// Convolves an image with the provided kernel. The kernel is assumed to be radially invariant, seperable and /// take the form {center value, value 1 pixel from center, value 2 pixels from center, etc.}. /// </summary> /// <typeparam name="I">Any image whose pixel values are stored as floats</typeparam> /// <param name="image">The image to convolve.</param> /// <param name="kernel">The kernel to use for convolution</param> /// <returns>A fitlered image</returns> public static I ConvolveHalf <I>(IArrayHandler <float> image, float[] kernel) where I : IArrayHandler <float>, new() { return(ConvolveHalf <I>(image, kernel, 1)); }
private static unsafe BinaryImage Hysteresis(GradientImage grad, IArrayHandler <byte> nms, float tlow, float thigh) { int r, c, pos, numedges, highcount; int[] hist = new int[short.MaxValue]; float maximum_mag, lowthreshold, highthreshold; maximum_mag = 0; float[,] magChannel = grad.ExtractChannel(0); short[] mag = new short[magChannel.Length]; fixed(short *dst = mag) { fixed(float *src = magChannel) { float *srcPtr = src; short *dstPtr = dst; for (int i = 0; i < mag.Length; i++, srcPtr++, dstPtr++) { *dstPtr = (short)(*srcPtr * 255); } } } int rows = grad.Rows; int cols = grad.Columns; byte[] edge = new byte[rows * cols]; fixed(byte *src = nms.RawArray) { byte *srcPtr = src; int length = rows * cols; for (pos = 0; pos < length; pos++) { if (*srcPtr++ == POSSIBLE_EDGE) { edge[pos] = POSSIBLE_EDGE; } else { edge[pos] = NOEDGE; } } } for (r = 0, pos = 0; r < rows; r++, pos += cols) { edge[pos] = NOEDGE; edge[pos + cols - 1] = NOEDGE; } pos = (rows - 1) * cols; for (c = 0; c < cols; c++, pos++) { edge[c] = NOEDGE; edge[pos] = NOEDGE; } for (r = 0; r < short.MaxValue; r++) { hist[r] = 0; } for (r = 0, pos = 0; r < rows; r++) { for (c = 0; c < cols; c++, pos++) { if (edge[pos] == POSSIBLE_EDGE) { hist[mag[pos]]++; } } } for (r = 1, numedges = 0; r < short.MaxValue; r++) { if (hist[r] != 0) { maximum_mag = (short)r; } numedges += hist[r]; } highcount = (int)(numedges * thigh + 0.5); r = 1; numedges = hist[1]; while ((r < (maximum_mag - 1)) && (numedges < highcount)) { r++; numedges += hist[r]; } highthreshold = (short)r; lowthreshold = (short)(highthreshold * tlow + 0.5); for (r = 0, pos = 0; r < rows; r++) { for (c = 0; c < cols; c++, pos++) { if ((edge[pos] == POSSIBLE_EDGE) && (mag[pos] >= highthreshold)) { edge[pos] = EDGE; follow_edges(edge, mag, pos, lowthreshold, cols); } } } for (r = 0, pos = 0; r < rows; r++) { for (c = 0; c < cols; c++, pos++) { if (edge[pos] != EDGE) { edge[pos] = NOEDGE; } } } BinaryImage edgeImage = new BinaryImage(rows, cols); fixed(bool *dst = edgeImage.RawArray) { bool *dstPtr = dst; int length = rows * cols; for (pos = 0; pos < length; pos++) { *dstPtr++ = edge[pos] == EDGE; } } return(edgeImage); }
/// <summary> /// Extracts a rectangle from a handler. /// </summary> /// <typeparam name="T">Underlying type of the handler</typeparam> /// <param name="handler">The handler used in extraction</param> /// <param name="rect">The rectangle to extract</param> /// <returns>The extracted rectangle</returns> public static T[, ,] ExtractRectangle <T>(this IArrayHandler <T> handler, Rectangle rect) { return(handler.ExtractRectangle(rect.R, rect.C, rect.Rows, rect.Columns)); }
/// <summary> /// Convolves an image with the provided kernels. These Kernels are full kernels, in that they go from /// a minimum value to a maximum value. There are no restrictions on what these kernels can be, though /// the user is cautioned to make sure that they are passing kernels which make sense, as this code /// does not check for any of the necessary kernel conditions. /// </summary> /// <typeparam name="I">Any image whose pixel values are stored as floats</typeparam> /// <param name="image">The image to convolve.</param> /// <param name="kernel">The kernel to use for convolution in the horizontal direction</param> /// <param name="subsample">The subsampling frequency</param> /// <returns>a fitlered image</returns> public static I ConvolveFull <I>(IArrayHandler <float> image, float[] kernel, int subsample) where I : IArrayHandler <float>, new() { return(ConvolveFull <I>(image, kernel, kernel, subsample)); }
/// <summary> /// Computes an integral image in one pass from the source image. /// </summary> /// <param name="input">Source image</param> /// <returns>Integral image</returns> public static unsafe T ComputeFloat <T>(IArrayHandler <float> input) where T : IArrayHandler <float>, new() { int rows = input.Rows; int columns = input.Columns; int channels = input.Channels; float[, ,] ii = new float[rows + 1, columns + 1, channels]; float[, ,] s = new float[rows + 1, columns + 1, channels]; //float[, ,] s2 = computeStandardDeviation ? new float[rows + 1, columns + 1, channels] : null; //float[, ,] ii2 = computeStandardDeviation ? new float[rows + 1, columns + 1, channels] : null; int stride = (columns + 1) * channels; fixed(float *iiScan = ii, sScan = s, /*s2Scan = s2, ii2Scan = ii2,*/ src = input.RawArray) { float *srcPtr = src; float *iiPtrM = iiScan + stride; float *iiPtr = iiPtrM + channels; float *sPtrM = sScan + channels; float *sPtr = sPtrM + stride; //float* ii2PtrM = computeStandardDeviation ? ii2Scan + stride : null; //float* ii2Ptr = computeStandardDeviation ? ii2PtrM + channels : null; //float* s2PtrM = computeStandardDeviation ? s2Scan + channels : null; //float* s2Ptr = computeStandardDeviation ? s2PtrM + stride : null; for (int r = 1; r < rows + 1; r++) { for (int c = 1; c < columns + 1; c++) { for (int i = 0; i < channels; i++) { //float assert = 0; //for (int rr = 0; rr < r; rr++) // for (int cc = 0; cc < c; cc++) // assert += input[rr, cc, i]; float val = *srcPtr++; //float val = input[r-1, c-1, i]; // normal *sPtr = *sPtrM + val; //s[r, c, i] = s[r - 1, c, i] + val; *iiPtr = *iiPtrM + *sPtr; //ii[r, c, i] = ii[r, c - 1, i] + s[r, c, i]; // squared //if (computeStandardDeviation) //{ // *s2Ptr = *s2PtrM + val * val; // //s2[r, c, i] = s2[r - 1, c, i] + val * val; // *ii2Ptr = *ii2PtrM + *s2Ptr; // //ii2[r, c, i] = ii2[r, c - 1, i] + s2[r, c, i]; //} sPtr++; iiPtr++; sPtrM++; iiPtrM++; //if (computeStandardDeviation) //{ // ii2PtrM++; // s2Ptr++; // ii2Ptr++; // s2PtrM++; //} //if (assert != ii[r, c, i]) // Console.WriteLine("hmmm"); } } sPtrM += channels; sPtr += channels; iiPtrM += channels; iiPtr += channels; //if (computeStandardDeviation) //{ // s2PtrM += channels; // s2Ptr += channels; // ii2PtrM += channels; // ii2Ptr += channels; //} } } //float[] stddev = new float[channels]; //if (computeStandardDeviation) //{ // for (int i = 0; i < channels; i++) // { // float sum = ii[rows, columns, i]; // float squaredSum = ii2[rows, columns, i]; // int count = rows * columns; // float mean = sum / count; // float variance = squaredSum / count - mean * mean; // stddev[i] = (float)Math.Sqrt(variance); // } //} s = null; GC.Collect(); T result = new T(); result.SetData(ii); result.IsIntegral = true; return(result); }
/// <summary> /// Convolves an image with the provided kernels. These Kernels are full kernels, in that they go from /// a minimum value to a maximum value. There are no restrictions on what these kernels can be, though /// the user is cautioned to make sure that they are passing kernels which make sense, as this code /// does not check for any of the necessary kernel conditions. /// </summary> /// <typeparam name="I">Any image whose pixel values are stored as floats</typeparam> /// <param name="image">The image to convolve.</param> /// <param name="kernelx">The kernel to use for convolution in the horizontal direction</param> /// <param name="kernely">The kernel to use for convolution in the vertical direction</param> /// <param name="subsample">The subsampling frequency</param> /// <returns>a fitlered image</returns> public static unsafe I ConvolveFull <I>(IArrayHandler <float> image, float[] kernelx, float[] kernely, int subsample) where I : IArrayHandler <float>, new() { int rows = image.Rows; int columns = image.Columns; int channels = image.Channels; int sizex = kernelx.Length; int halfx = sizex / 2; int sizey = kernely.Length; int halfy = sizey / 2; float[, ,] dest = new float[rows, columns, channels]; fixed(float *src = image.RawArray, dst = dest, knl = kernely) { float *srcPtr = src; float *dstPtr = dst; int stride = columns * channels; for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { for (int i = 0; i < channels; i++, srcPtr++) { float *knlPtr = knl; int diff = -halfy; if (r + diff < 0) { diff = -r; } float *srcScan = srcPtr + diff * stride; float sum = 0; for (int k = 0, rr = r - halfy; k < sizey; k++, knlPtr++, rr++) { sum += *knlPtr * *srcScan; if (rr >= 0 && rr < rows - 1) { srcScan += stride; } } *dstPtr++ = sum; } } } } int nrows = rows / subsample; int ncolumns = columns / subsample; float[, ,] source = dest; dest = new float[nrows, ncolumns, channels]; fixed(float *src = source, dst = dest, knl = kernelx) { float *srcPtr = src; float *dstPtr = dst; int stride = columns * channels; for (int r = 0, tr = 0; r < nrows; r++, tr += subsample, srcPtr += subsample * stride) { float *srcScan = srcPtr; for (int c = 0, tc = 0; c < ncolumns; c++, tc += subsample, srcScan += channels * (subsample - 1)) { for (int i = 0; i < channels; i++, srcScan++) { float *knlPtr = knl; int diff = -halfx; if (tc + diff < 0) { diff = -tc; } float *srcScan1 = srcScan + diff * channels; float sum = 0; for (int k = 0, cc = tc - halfx; k < sizex; k++, knlPtr++, cc++) { sum += *knlPtr * *srcScan1; if (cc >= 0 && cc < columns - 1) { srcScan1 += channels; } } *dstPtr++ = sum; } } } } I result = new I(); result.SetData(dest); return(result); }
/// <summary> /// Convolves an image with the provided kernels. Both kernels are assumed to be radially invariant, seperable and /// take the form {center value, value 1 pixel from center, value 2 pixels from center, etc.}. The result is /// sub-sampled using the provided frequency. /// </summary> /// <typeparam name="I">Any image whose pixel values are stored as floats</typeparam> /// <param name="image">The image to convolve.</param> /// <param name="kernelx">The kernel to use for convolution in the horizontal direction</param> /// <param name="kernely">The kernel to use for convolution in the vertical direction</param> /// <param name="subsample">The subsampling frequency</param> /// <returns>a fitlered image</returns> public static unsafe I ConvolveHalf <I>(IArrayHandler <float> image, float[] kernelx, float[] kernely, int subsample) where I : IArrayHandler <float>, new() { int rows = image.Rows; int columns = image.Columns; int channels = image.Channels; int sizex = kernelx.Length; int sizey = kernely.Length; float[, ,] dest = new float[rows, columns, channels]; fixed(float *src = image.RawArray, dst = dest, knl = kernely) { float *srcPtr = src; float *dstPtr = dst; int stride = columns * channels; for (int r = 0; r < rows; r++) { for (int c = 0; c < columns; c++) { for (int i = 0; i < channels; i++, srcPtr++, dstPtr++) { float *knlPtr = knl; float sum = *knlPtr * *srcPtr; knlPtr++; for (int k = 1; k < sizey; k++, knlPtr++) { int diff = k * stride; int nr = r - k; int pr = r + k; if (nr < 0) { sum += 2 * (*knlPtr * *(srcPtr + diff)); } else if (pr >= rows) { sum += 2 * (*knlPtr * *(srcPtr - diff)); } else { sum += *knlPtr * *(srcPtr - diff) + *knlPtr * *(srcPtr + diff); } } *dstPtr = sum; } } } } int nrows = rows / subsample; int ncolumns = columns / subsample; float[,,] source = dest; dest = new float[nrows, ncolumns, channels]; fixed(float *src = source, dst = dest, knl = kernelx) { int stride = columns * channels; float *srcPtr = src + stride * (subsample / 2) + channels * (subsample / 2); float *dstPtr = dst; for (int r = 0; r < nrows; r++, srcPtr += subsample * stride) { float *srcScan = srcPtr; for (int c = 0; c < ncolumns; c++, srcScan += channels * (subsample - 1)) { for (int i = 0; i < channels; i++, srcScan++, dstPtr++) { float *knlPtr = knl; float sum = *knlPtr * *srcScan; knlPtr++; for (int k = 1; k < sizex; k++, knlPtr++) { int nc = c - k; int pc = c + k; int diff = k * channels; if (nc < 0) { sum += 2 * (*knlPtr * *(srcScan + diff)); } else if (pc >= columns) { sum += 2 * (*knlPtr * *(srcScan - diff)); } else { sum += *knlPtr * *(srcScan + diff) + *knlPtr * *(srcScan - diff); } } *dstPtr = sum; } } } } I result = new I(); result.SetData(dest); return(result); }
/// <summary> /// Convolves the provided image with a two dimensional Gaussian of the provided sigma and returns the result. /// </summary> /// <typeparam name="I">Any image whose pixel values are stored as floats</typeparam> /// <param name="image">The image to convolve.</param> /// <param name="sigma">The sigma to use in the Gaussian</param> /// <returns>A blurred image</returns> public static I ConvolveGaussian <I>(IArrayHandler <float> image, float sigma) where I : IArrayHandler <float>, new() { return(ConvolveGaussian <I>(image, sigma, 1)); }
/// <summary> /// Convolves the provided image with a two dimensional Gaussian of the provided sigma and returns the result, /// subsampled as directed. /// </summary> /// <typeparam name="I">Any image whose pixel values are stored as floats</typeparam> /// <param name="image">The image to convolve.</param> /// <param name="sigma">The sigma to use in the Gaussian</param> /// <param name="subsample">The subsampling frequency.</param> /// <returns>A blurred image</returns> public static I ConvolveGaussian <I>(IArrayHandler <float> image, float sigma, int subsample) where I : IArrayHandler <float>, new() { I result = ConvolveHalf <I>(image, Gaussian.ComputeHalfKernel(sigma), subsample); return(result); }