C#在带公差的图像中查找图像

C#在带公差的图像中查找图像,c#,image,image-processing,bitmap,C#,Image,Image Processing,Bitmap,我有一个函数,它可以在一个较大的图像中找到一个较小的图像,并返回它的位置。我想设置一个容差阈值,这样即使存在相似(但不完全相同)的对象,它也会返回其位置 如果可能的话,我也希望它的工作,如果较小的图像是旋转 我尝试使用OpenCV在图像中查找边缘,但“干草堆”中的边缘和“针”图像中的边缘从来都不完全相同,也从不匹配 public Point? Find(Bitmap haystack, Bitmap needle) { if (null == haystack || null == ne

我有一个函数,它可以在一个较大的图像中找到一个较小的图像,并返回它的位置。我想设置一个容差阈值,这样即使存在相似(但不完全相同)的对象,它也会返回其位置

如果可能的话,我也希望它的工作,如果较小的图像是旋转

我尝试使用OpenCV在图像中查找边缘,但“干草堆”中的边缘和“针”图像中的边缘从来都不完全相同,也从不匹配

public Point? Find(Bitmap haystack, Bitmap needle)
{
    if (null == haystack || null == needle)
    {
        return null;
    }
    if (haystack.Width < needle.Width || haystack.Height < needle.Height)
    {
        return null;
    }

    var haystackArray = GetPixelArray(haystack);
    var needleArray = GetPixelArray(needle);

    foreach (var firstLineMatchPoint in FindMatch(haystackArray.Take(haystack.Height - needle.Height), needleArray[0]))
    {
        if (IsNeedlePresentAtLocation(haystackArray, needleArray, firstLineMatchPoint, 1))
        {
            return firstLineMatchPoint;
        }
    }

    return null;
}

private int[][] GetPixelArray(Bitmap bitmap)
{
    var result = new int[bitmap.Height][];
    var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly,
        PixelFormat.Format32bppArgb);

    for (int y = 0; y < bitmap.Height; ++y)
    {
        result[y] = new int[bitmap.Width];
        Marshal.Copy(bitmapData.Scan0 + y*bitmapData.Stride, result[y], 0, result[y].Length);
    }

    bitmap.UnlockBits(bitmapData);

    return result;
}

private IEnumerable<Point> FindMatch(IEnumerable<int[]> haystackLines, int[] needleLine)
{
    var y = 0;
    foreach (var haystackLine in haystackLines)
    {
        for (int x = 0, n = haystackLine.Length - needleLine.Length; x < n; ++x)
        {
            if (ContainSameElements(haystackLine, x, needleLine, 0, needleLine.Length))
            {
                yield return new Point(x, y);
            }
        }
        y += 1;
    }
}

private bool ContainSameElements(int[] first, int firstStart, int[] second, int secondStart, int length)
{
    for (int i = 0; i < length; ++i)
    {
        if (first[i + firstStart] != second[i + secondStart])
        {
            return false;
        }
    }
    return true;
}

private bool IsNeedlePresentAtLocation(int[][] haystack, int[][] needle, Point point, int alreadyVerified)
{
    //we already know that "alreadyVerified" lines already match, so skip them
    for (int y = alreadyVerified; y < needle.Length; ++y)
    {
        if ( ! ContainSameElements(haystack[y + point.Y], point.X, needle[y], 0, needle.Length))
        {
            return false;
        }
    }
    return true;
}
公共点?查找(位图草堆、位图针)
{
如果(空==草堆| |空==针)
{
返回null;
}
if(干草堆.宽度<针.宽度| |干草堆.高度<针.高度)
{
返回null;
}
var haystackArray=GetPixelArray(haystack);
var neederarray=GetPixelArray(针);
foreach(FindMatch中的var firstLineMatchPoint(haystackArray.Take(haystack.Height-needle.Height),needArray[0]))
{
if(IsNeedlePresentAtLocation(haystackArray、needleArray、firstLineMatchPoint,1))
{
返回firstLineMatchPoint;
}
}
返回null;
}
私有int[]GetPixelArray(位图)
{
var result=newint[bitmap.Height][];
var bitmapData=bitmap.LockBits(新矩形(0,0,bitmap.Width,bitmap.Height),ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
对于(int y=0;y

如何实现这一点?

首先,您必须定义用于比较两个像素的度量

我可以想象RGB定义的立方体中的距离,或者HSV圆柱体中的距离(HSV应该更精确) 例如:

static double GetMetric(像素a、像素b)
{
双dR=a.R-b.R;
双dG=a.G-b.G;
双分贝=a.B-B.B;
返回Math.Sqrt(dR*dR+dG*dG+dB*dB);
}
然后创建一个窗口搜索算法。在haste中创建窗口(与针大小相同)。然后尝试窗口的每个可能位置,并将窗口距离计算为像素距离之和

您不需要重新计算整个窗口。将窗口向右移动时,只需重新计算并减去左列(已删除的列),然后计算并添加右列(新列)

然后你需要记住最小距离和它的位置,然后比较它

结果是距离针最近的窗口。(取决于使用的度量)

一个简化的例子:

静态无效查找(位图快速、位图指针)
{
//简化的速度。高度=针高度
//但是要快。宽度>针。宽度
int minX=0;
int minY=0;
double minMetric=double.MaxValue;
//设置第一个窗口
双实际计量=0;
对于(int i=0;i
但这不适用于灰度图像。这也会匹配许多不同的具有相似颜色比率的图像,我如何将其用于rgb?举个例子?@mrid这正好找到了大一点的smalle rimage。它在haste中找到最近的窗口(较小的图像)。您能提供一个如何使用该窗口的示例吗?它用于分析手写数字或字母(例如,手写3的度量值接近整数3,也接近整数8,您永远无法从度量值获得100%匹配)