/// <summary> /// Process the filter on the specified image. /// </summary> /// /// <param name="image">Source image data.</param> /// protected override void ProcessFilter(UnmanagedImage image) { // Apply first Gaussian blur var image1 = first.Apply(image); // Apply second Gaussian blur second.ApplyInPlace(image); // Subtract the two images subtract.UnmanagedOverlayImage = image1; subtract.ApplyInPlace(image); }
/// <summary> /// Process the filter on the specified image. /// </summary> /// /// <param name="source">Source image data.</param> /// <param name="destination">Destination image data.</param> /// <param name="rect">Image rectangle for processing by the filter.</param> /// protected override unsafe void ProcessFilter(UnmanagedImage source, UnmanagedImage destination, Rectangle rect) { // processing start and stop X,Y positions int startX = rect.Left + 1; int startY = rect.Top + 1; int stopX = startX + rect.Width - 2; int stopY = startY + rect.Height - 2; int width = rect.Width - 2; int height = rect.Height - 2; int dstStride = destination.Stride; int srcStride = source.Stride; int dstOffset = dstStride - rect.Width + 2; int srcOffset = srcStride - rect.Width + 2; // pixel's value and gradients int gx, gy; // double orientation, toAngle = 180.0 / System.Math.PI; float leftPixel = 0, rightPixel = 0; // STEP 1 - blur image UnmanagedImage blurredImage = gaussianFilter.Apply(source); // orientation array byte[] orients = new byte[width * height]; // gradients array float[,] gradients = new float[source.Width, source.Height]; float maxGradient = float.NegativeInfinity; // do the job byte *src = (byte *)blurredImage.ImageData.ToPointer( ); // allign pointer src += srcStride * startY + startX; // STEP 2 - calculate magnitude and edge orientation int p = 0; // for each line for (int y = startY; y < stopY; y++) { // for each pixel for (int x = startX; x < stopX; x++, src++, p++) { gx = src[-srcStride + 1] + src[srcStride + 1] - src[-srcStride - 1] - src[srcStride - 1] + 2 * (src[1] - src[-1]); gy = src[-srcStride - 1] + src[-srcStride + 1] - src[srcStride - 1] - src[srcStride + 1] + 2 * (src[-srcStride] - src[srcStride]); // get gradient value gradients[x, y] = (float)Math.Sqrt(gx * gx + gy * gy); if (gradients[x, y] > maxGradient) { maxGradient = gradients[x, y]; } // --- get orientation if (gx == 0) { // can not divide by zero orientation = (gy == 0) ? 0 : 90; } else { double div = (double)gy / gx; // handle angles of the 2nd and 4th quads if (div < 0) { orientation = 180 - System.Math.Atan(-div) * toAngle; } // handle angles of the 1st and 3rd quads else { orientation = System.Math.Atan(div) * toAngle; } // get closest angle from 0, 45, 90, 135 set if (orientation < 22.5) { orientation = 0; } else if (orientation < 67.5) { orientation = 45; } else if (orientation < 112.5) { orientation = 90; } else if (orientation < 157.5) { orientation = 135; } else { orientation = 0; } } // save orientation orients[p] = (byte)orientation; } src += srcOffset; } // STEP 3 - suppres non maximums byte *dst = (byte *)destination.ImageData.ToPointer( ); // allign pointer dst += dstStride * startY + startX; p = 0; // for each line for (int y = startY; y < stopY; y++) { // for each pixel for (int x = startX; x < stopX; x++, dst++, p++) { // get two adjacent pixels switch (orients[p]) { case 0: leftPixel = gradients[x - 1, y]; rightPixel = gradients[x + 1, y]; break; case 45: leftPixel = gradients[x - 1, y + 1]; rightPixel = gradients[x + 1, y - 1]; break; case 90: leftPixel = gradients[x, y + 1]; rightPixel = gradients[x, y - 1]; break; case 135: leftPixel = gradients[x + 1, y + 1]; rightPixel = gradients[x - 1, y - 1]; break; } // compare current pixels value with adjacent pixels if ((gradients[x, y] < leftPixel) || (gradients[x, y] < rightPixel)) { *dst = 0; } else { *dst = (byte)(gradients[x, y] / maxGradient * 255); } } dst += dstOffset; } // STEP 4 - hysteresis dst = (byte *)destination.ImageData.ToPointer( ); // allign pointer dst += dstStride * startY + startX; // for each line for (int y = startY; y < stopY; y++) { // for each pixel for (int x = startX; x < stopX; x++, dst++) { if (*dst < highThreshold) { if (*dst < lowThreshold) { // non edge *dst = 0; } else { // check 8 neighboring pixels if ((dst[-1] < highThreshold) && (dst[1] < highThreshold) && (dst[-dstStride - 1] < highThreshold) && (dst[-dstStride] < highThreshold) && (dst[-dstStride + 1] < highThreshold) && (dst[dstStride - 1] < highThreshold) && (dst[dstStride] < highThreshold) && (dst[dstStride + 1] < highThreshold)) { *dst = 0; } } } } dst += dstOffset; } // STEP 5 - draw black rectangle to remove those pixels, which were not processed // (this needs to be done for those cases, when filter is applied "in place" - // source image is modified instead of creating new copy) Drawing.Rectangle(destination, rect, Color.Black); // release blurred image blurredImage.Dispose( ); }