public TemplateMatch[] ProcessImage( UnmanagedImage image, UnmanagedImage template, Rectangle searchZone )
{
// check image format
if (
( ( image.PixelFormat != PixelFormat.Format8bppIndexed ) &&
( image.PixelFormat != PixelFormat.Format24bppRgb ) ) ||
( image.PixelFormat != template.PixelFormat ) )
{
throw new UnsupportedImageFormatException( "Unsupported pixel format of the source or template image." );
}
// clip search zone
Rectangle zone = searchZone;
zone.Intersect( new Rectangle( 0, 0, image.Width, image.Height ) );
// search zone's starting point
int startX = zone.X;
int startY = zone.Y;
// get source and template image size
int sourceWidth = zone.Width;
int sourceHeight = zone.Height;
int templateWidth = template.Width;
int templateHeight = template.Height;
// check template's size
if ( ( templateWidth > sourceWidth ) || ( templateHeight > sourceHeight ) )
{
throw new InvalidImagePropertiesException( "Template's size should be smaller or equal to search zone." );
}
int pixelSize = ( image.PixelFormat == PixelFormat.Format8bppIndexed ) ? 1 : 3;
int sourceStride = image.Stride;
// similarity map. its size is increased by 4 from each side to increase
// performance of non-maximum suppresion
int mapWidth = sourceWidth - templateWidth + 1;
int mapHeight = sourceHeight - templateHeight + 1;
int[,] map = new int[mapHeight + 4, mapWidth + 4];
// maximum possible difference with template
int maxDiff = templateWidth * templateHeight * pixelSize * 255;
// integer similarity threshold
int threshold = (int) ( similarityThreshold * maxDiff );
// width of template in bytes
int templateWidthInBytes = templateWidth * pixelSize;
// do the job
unsafe
{
byte* baseSrc = (byte*) image.ImageData.ToPointer( );
byte* baseTpl = (byte*) template.ImageData.ToPointer( );
int sourceOffset = image.Stride - templateWidth * pixelSize;
int templateOffset = template.Stride - templateWidth * pixelSize;
// for each row of the source image
for ( int y = 0; y < mapHeight; y++ )
{
// for each pixel of the source image
for ( int x = 0; x < mapWidth; x++ )
{
byte* src = baseSrc + sourceStride * ( y + startY ) + pixelSize * ( x + startX );
byte* tpl = baseTpl;
// compare template with source image starting from current X,Y
int dif = 0;
// for each row of the template
for ( int i = 0; i < templateHeight; i++ )
{
// for each pixel of the template
for ( int j = 0; j < templateWidthInBytes; j++, src++, tpl++ )
{
int d = *src - *tpl;
if ( d > 0 )
{
dif += d;
}
else
{
dif -= d;
}
}
src += sourceOffset;
tpl += templateOffset;
}
// templates similarity
int sim = maxDiff - dif;
if ( sim >= threshold )
map[y + 2, x + 2] = sim;
}
}
}
// collect interesting points - only those points, which are local maximums
List<TemplateMatch> matchingsList = new List<TemplateMatch>( );
// for each row
for ( int y = 2, maxY = mapHeight + 2; y < maxY; y++ )
{
// for each pixel
for ( int x = 2, maxX = mapWidth + 2; x < maxX; x++ )
{
int currentValue = map[y, x];
// for each windows' row
for ( int i = -2; ( currentValue != 0 ) && ( i <= 2 ); i++ )
{
// for each windows' pixel
for ( int j = -2; j <= 2; j++ )
{
if ( map[y + i, x + j] > currentValue )
{
currentValue = 0;
break;
}
}
}
// check if this point is really interesting
if ( currentValue != 0 )
{
matchingsList.Add( new TemplateMatch(
new Rectangle( x - 2 + startX, y - 2 + startY, templateWidth, templateHeight ),
(float) currentValue / maxDiff ) );
}
}
}
// convert list to array
TemplateMatch[] matchings = new TemplateMatch[matchingsList.Count];
matchingsList.CopyTo( matchings );
// sort in descending order
Array.Sort( matchings, new MatchingsSorter( ) );
return matchings;
}