好吧,对于具有已知输入格式的如此简单的东西(即图像中只有一个与背景之间具有高对比度的圆圈),您可以很容易地暴力破解它。只需遍历图像数据并寻找这些对比度差异。保存最顶部、最左侧、最右侧和最底部的位置,您就完成了。
如果图像不是这样简单的格式,那么您将需要更高级的东西,例如blob detection algorithm。
编辑:仅供参考,前段时间我写了这个蛮力算法来做一些非常相似的事情。它远非完美且没有高度优化,尽管它很简单并且方法应该清晰。我将在这里发布整个内容(不要太苛刻地评价我;我是几年前在自学 C# 时写的)。该算法使用强度阈值来查找圆(与对比度相反,我的输入非常明确)。
/// <summary>
/// Locates the spot of light on the image and returns an AnalysisResults object.
/// </summary>
unsafe private Rectangle AnalyzeImage( )
{
// function assumes 24bpp RGB format
Bitmap image = m_originalImage;
if ( image.PixelFormat != PixelFormat.Format24bppRgb )
{
throw new ArgumentException( "Image format must be 24bpp RGB" );
}
// using the GDI+ GetPixel method is too slow for a
// 1280x1024 image, so get directly at the image buffer instead.
GraphicsUnit unit = GraphicsUnit.Pixel;
imageRect = Rectangle.Truncate( image.GetBounds( ref unit ) );
BitmapData data = image.LockBits( imageRect, ImageLockMode.ReadWrite, image.PixelFormat );
int intensityThreshold = IntensityThreshold;
// initialize 'top' to -1 so that we can check if it has been set before setting it.
// once a valid value for 'top' is found we don't need to set it again.
int top = -1;
// search for the left point starting at a high value so that we can simply
// pull it towards the left as we find pixels inside of the spot.
int left = imageRect.Right;
int bottom = 0;
int right = 0;
// locate the circle in the image by finding pixels with average
// intesity values above the threshold and then performing
// some edge checks to set the top, left, right, and bottom values.
int height = imageRect.Height + imageRect.Y;
int width = imageRect.Width + imageRect.X;
byte* pSrc = ( byte* ) data.Scan0;
int rowOffset = 1;
for ( int y = imageRect.Y ; y < height ; ++y, ++rowOffset )
{
for ( int x = imageRect.X ; x < width ; ++x )
{
// windows stores images in memory in reverse byte order ( BGR )
byte b = *pSrc++;
byte g = *pSrc++;
byte r = *pSrc++;
// get the average intensity and see if it is above the threshold
int intensity = GetIntensity( r, g, b );
if ( intensity > intensityThreshold )
{
if ( !StrayPixel( pSrc, data, intensityThreshold ) )
{
// found a point in the circle
if ( top == -1 ) top = y;
if ( x < left ) left = x;
if ( y > bottom ) bottom = y;
if ( x > right ) right = x;
}
}
}
// next row
pSrc = ( ( byte* ) data.Scan0 ) + ( rowOffset * data.Stride );
}
image.UnlockBits( data );
// bounding rectangle of our spot
return Rectangle.FromLTRB( left, top, right, bottom );
}
/// <summary>
/// Returns true if the pixel at (x,y) is surrounded in four
/// directions by pixels that are below the specified intesity threshold.
/// This method only checks the first pixel above, below, left, and right
/// of the location currently pointed to by 'pSrc'.
/// </summary>
private unsafe bool StrayPixel( byte* pSrc, BitmapData data, int intensityThreshold )
{
// this method uses raw pointers instead of GetPixel because
// the original image is locked and this is the only way to get at the data.
// if we have a pixel with a relatively high saturation
// value we can safely assume that it is a camera artifact.
if ( Color.FromArgb( pSrc[ 2 ], pSrc[ 1 ], *pSrc ).GetSaturation( ) > MAX_PIXEL_SAT )
{
return true;
}
byte* pAbove = pSrc - data.Stride;
int above = GetIntensity( pAbove[ 2 ], pAbove[ 1 ], *pAbove );
byte* pRight = pSrc + 3;
int right = GetIntensity( pRight[ 2 ], pRight[ 1 ], *pRight );
byte* pBelow = pSrc + data.Stride;
int below = GetIntensity( pBelow[ 2 ], pBelow[ 1 ], *pBelow );
byte* pLeft = pSrc - 3;
int left = GetIntensity( pLeft[ 2 ], pLeft[ 1 ], *pLeft );
// if all of the surrounding pixels are below the threshold we have found a stray
return above < intensityThreshold &&
right < intensityThreshold &&
below < intensityThreshold &&
left < intensityThreshold;
}
/// <summary>
/// Returns the average of ( r, g, b )
/// </summary>
private int GetIntensity( byte r, byte g, byte b )
{
return GetIntensity( Color.FromArgb( r, g, b ) );
}
/// <summary>
/// Returns the average of ( c.r, c.g, c.b )
/// </summary>
private int GetIntensity( Color c )
{
return ( c.R + c.G + c.B ) / 3;
}