0

我目前将位图像素数据存储在一个字符数组中。我想知道根据图像的边界框裁剪图像的最有效算法是什么。

我在下面提供了一个相对准确的示例来说明我想要实现的目标。基于基本“像素颜色”。

边界框示例

边界框示例

4

3 回答 3

3

蛮力很好,但你可以更好地使用加速StretchBlt来计算水平和垂直投影。

获取位图,将其绘制到一个 1 像素高的全宽矩形上。

获取位图,将其绘制到一个 1 像素宽的全高矩形上。

两者都必须处理整个图像,但将使用高度并行的 GPU 加速渲染来完成。

从这些计算界限。

好的,如果整列的平均值恰好是背景颜色,则结果可能会出现错误。

于 2012-06-14T20:21:32.073 回答
0

好吧,对于具有已知输入格式的如此简单的东西(即,图像中只有一个与背景之间具有高对比度的圆圈),您可以很容易地暴力破解它。只需遍历图像数据并寻找这些对比度差异。保存最顶部、最左侧、最右侧和最底部的位置,您就完成了。

如果图像不是这样简单的格式,那么您将需要更高级的东西,例如 斑点检测算法

编辑:仅供参考,前段时间我写了这个蛮力算法来做一些非常相似的事情。它远非完美且没有高度优化,尽管它很简单并且方法应该清晰。我将在这里发布整个内容(不要太苛刻地评价我;我是几年前在自学 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;
}
于 2012-06-14T18:39:59.617 回答
0

晚上好游戏!谢谢!快速编写了一些 C# 代码来执行此蛮力:

public class Dimensions
{
    public int left = 0;
    public int right = 0;
    public int top = 0;
    public int bottom = 0;
}

public class ImageDetection
{
    private const int xSize = 2000;
    private const int ySize = 2000;

    private const int xMin = 1800;
    private const int yMin = 1800;
    private const int defaultPixelColor = 0;


    public void GetArray(out char[,] arr)
    {
        arr = new char[xSize, ySize];
        Random rand = new Random();

        for (int i=0; i<xSize; ++i)
        {
            for (int j=0; j<ySize; ++j)
            {
                var d = rand.NextDouble();

                if (d < 0.5)
                {
                    arr[i, j] = Convert.ToChar(defaultPixelColor);
                }
                else
                {
                    int theInt = Convert.ToInt32(255 * d);
                    arr[i, j] = Convert.ToChar(theInt);
                }
            }
        }

        // cut top
        for (int k = 0; k < (xSize - xMin); k++)
        {
            for (int l = 0; l < ySize; l++)
            {
                arr[k, l] = Convert.ToChar(defaultPixelColor);
            }
        }

        // cut bottom
        for (int k = xMin; k < xSize; k++)
        {
            for (int l = 0; l < ySize; l++)
            {
                arr[k, l] = Convert.ToChar(defaultPixelColor);
            }
        }

        // cut left
        for (int k = 0; k < xSize; k++)
        {
            for (int l = 0; l < (ySize - xMin); l++)
            {
                arr[k, l] = Convert.ToChar(defaultPixelColor);
            }
        }

        // cut right 
        for (int k = 0; k < xSize; k++)
        {
            for (int l = xMin; l < ySize; l++)
            {
                arr[k, l] = Convert.ToChar(defaultPixelColor);
            }
        }

    }

    public void WriteArr(ref char[,] arr)
    {
        char[] line = new char[xSize];

        // all lines
        for (int i=0; i<ySize; ++i)
        {
            // build one line
            for (int j = 0; j < xSize; ++j)
            {
                char curChar = arr[i, j];

                if (curChar == '\0')
                {
                    line[j] = '.';
                }
                else
                {
                    line[j] = curChar;
                }
            }

            string s = new string(line);
            s += "\r\n";
            //FileIO.WriteFileText
            System.IO.File.AppendAllText("Matrix.txt", s);
         }
    }

    public void DetectSize(ref char[,] arr, out Dimensions dim)
    {
        dim = new Dimensions();

        dim.left = xSize;
        dim.top = ySize;
        dim.right = 0;
        dim.bottom = 0;

        for (int i = 0; i < xSize; ++i)
        {
            for (int j = 0; j < ySize; ++j)
            {
                if (!arr[i, j].Equals(Convert.ToChar(defaultPixelColor)))
                {
                    if (i < dim.left)
                    {
                        dim.left = i;
                    }

                    if (j < dim.top)
                    {
                        dim.top = j;
                    }

                    if (i > dim.right)
                    {
                        dim.right = i;
                    }

                    if (j > dim.bottom)
                    {
                        dim.bottom = j;
                    }
                }
            }
        }
    }
}
于 2012-06-14T19:53:22.767 回答