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( );
}
}