在另一个具有缩进和 return 坐标的位图中搜索位图
Search for a bitmap in another bitmap with indentation and return coordinates
我找到了一个完美的功能,但不知道如何添加搜索缩进:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
for (int outerX = 0; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = 0; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:
continue;
}
}
location = Point.Empty;
return false;
}
我通过添加变量 xFrom 和 yFrom 修改了函数
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom, int yFrom)
并设置计数器的初始值:outerX和outerY
但是没用。不明白怎么修改功能
让我们从一个完整的解决方案开始,它使用您按原样提供的函数:
Bitmap fullImage = (Bitmap)Bitmap.FromFile(@"G:\large.png");
Bitmap smallImage = (Bitmap)Bitmap.FromFile(@"G:\small.png");
if (FindBitmap(smallImage, fullImage, out Point location))
{
Console.WriteLine($"Found small image embedded at {location}");
}
else
{
Console.WriteLine("Couldn't find small image embedded in larger image.");
}
我 运行 使用一些示例数据并能够找到嵌入的图像。在我的示例中,我的嵌入图像位于 {X=88,Y=74}
,并且在我的 CPU 上需要大约 330k 个滴答才能找到这个位置。现在,问题是如何修改您的原始函数以提供搜索范围 space。正如您所建议的,您可以为您的函数设置一个初始的 x 和 y 位置,作为猜测或缩进提供。
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom = 0, int yFrom = 0)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
for (int outerX = xFrom; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = yFrom; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:
continue;
}
}
location = Point.Empty;
return false;
}
现在我们可以调用 with 进行更好的猜测:if (FindBitmap(smallImage, fullImage, out Point location, 80, 70))
与 returns 相同的值。由于搜索 space 减少了,现在我的机器上只需要 45k 个滴答。您可以类似地添加 xTo
和 yTo
值以进一步限制搜索 space.
但是,我们可以做得更好!现在您正在使用 GetPixel
,这是出了名的低效。您可以锁定位图图像并直接访问像素数据:
public static int[] GetPixels(Bitmap image)
{
var imageLock = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int[] pixels = new int[image.Width * image.Height];
Marshal.Copy(imageLock.Scan0, pixels, 0, pixels.Length);
image.UnlockBits(imageLock);
return pixels;
}
现在您可以在搜索开始时抓取像素数据并直接访问它:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom = 0, int yFrom = 0)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
var needlePixels = GetPixels(bmpNeedle);
var haystackPixels = GetPixels(bmpHaystack);
for (int outerX = xFrom; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = yFrom; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
var cNeedle = needlePixels[innerX + innerY * bmpNeedle.Width];
var cHaystack = haystackPixels[innerX + outerX + (innerY + outerY) * bmpHaystack.Width];
if (cNeedle != cHaystack)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:
continue;
}
}
location = Point.Empty;
return false;
}
这将我的硬件上的搜索时间减少到 25k 次,并且可能会为更大的搜索带来好处。最后,您可以重新格式化这段代码,使其(我希望)更干净一些。这是我建议的最终方法,它在我的机器上只需要大约 15k 个滴答:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom = 0, int yFrom = 0)
{
location = Point.Empty;
if (bmpNeedle == null || bmpHaystack == null)
{
return false;
}
var needlePixels = GetPixels(bmpNeedle);
var haystackPixels = GetPixels(bmpHaystack);
for (int outerX = xFrom; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = yFrom; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
if (IsMatch(needlePixels, haystackPixels, outerX, outerY, bmpNeedle.Width, bmpHaystack.Width))
{
location = new Point(outerX, outerY);
return true;
}
}
}
return false;
}
public static bool IsMatch(int[] needle, int[] haystack, int x, int y, int needleWidth, int haystackWidth)
{
for (int i = 0; i < needle.Length; i++)
{
var innerX = i % needleWidth;
var innerY = i / needleWidth;
var cNeedle = needle[i];
var cHaystack = haystack[innerX + x + (innerY + y) * haystackWidth];
if (cNeedle != cHaystack) return false;
}
return true;
}
我已经修改了您的代码,以便您可以使用搜索缩进:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int startX, int startY)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
for (int outerX = startX; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = startY; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:;
}
}
location = Point.Empty;
return false;
}
我找到了一个完美的功能,但不知道如何添加搜索缩进:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
for (int outerX = 0; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = 0; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:
continue;
}
}
location = Point.Empty;
return false;
}
我通过添加变量 xFrom 和 yFrom 修改了函数
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom, int yFrom)
并设置计数器的初始值:outerX和outerY
但是没用。不明白怎么修改功能
让我们从一个完整的解决方案开始,它使用您按原样提供的函数:
Bitmap fullImage = (Bitmap)Bitmap.FromFile(@"G:\large.png");
Bitmap smallImage = (Bitmap)Bitmap.FromFile(@"G:\small.png");
if (FindBitmap(smallImage, fullImage, out Point location))
{
Console.WriteLine($"Found small image embedded at {location}");
}
else
{
Console.WriteLine("Couldn't find small image embedded in larger image.");
}
我 运行 使用一些示例数据并能够找到嵌入的图像。在我的示例中,我的嵌入图像位于 {X=88,Y=74}
,并且在我的 CPU 上需要大约 330k 个滴答才能找到这个位置。现在,问题是如何修改您的原始函数以提供搜索范围 space。正如您所建议的,您可以为您的函数设置一个初始的 x 和 y 位置,作为猜测或缩进提供。
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom = 0, int yFrom = 0)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
for (int outerX = xFrom; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = yFrom; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:
continue;
}
}
location = Point.Empty;
return false;
}
现在我们可以调用 with 进行更好的猜测:if (FindBitmap(smallImage, fullImage, out Point location, 80, 70))
与 returns 相同的值。由于搜索 space 减少了,现在我的机器上只需要 45k 个滴答。您可以类似地添加 xTo
和 yTo
值以进一步限制搜索 space.
但是,我们可以做得更好!现在您正在使用 GetPixel
,这是出了名的低效。您可以锁定位图图像并直接访问像素数据:
public static int[] GetPixels(Bitmap image)
{
var imageLock = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int[] pixels = new int[image.Width * image.Height];
Marshal.Copy(imageLock.Scan0, pixels, 0, pixels.Length);
image.UnlockBits(imageLock);
return pixels;
}
现在您可以在搜索开始时抓取像素数据并直接访问它:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom = 0, int yFrom = 0)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
var needlePixels = GetPixels(bmpNeedle);
var haystackPixels = GetPixels(bmpHaystack);
for (int outerX = xFrom; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = yFrom; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
var cNeedle = needlePixels[innerX + innerY * bmpNeedle.Width];
var cHaystack = haystackPixels[innerX + outerX + (innerY + outerY) * bmpHaystack.Width];
if (cNeedle != cHaystack)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:
continue;
}
}
location = Point.Empty;
return false;
}
这将我的硬件上的搜索时间减少到 25k 次,并且可能会为更大的搜索带来好处。最后,您可以重新格式化这段代码,使其(我希望)更干净一些。这是我建议的最终方法,它在我的机器上只需要大约 15k 个滴答:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int xFrom = 0, int yFrom = 0)
{
location = Point.Empty;
if (bmpNeedle == null || bmpHaystack == null)
{
return false;
}
var needlePixels = GetPixels(bmpNeedle);
var haystackPixels = GetPixels(bmpHaystack);
for (int outerX = xFrom; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = yFrom; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
if (IsMatch(needlePixels, haystackPixels, outerX, outerY, bmpNeedle.Width, bmpHaystack.Width))
{
location = new Point(outerX, outerY);
return true;
}
}
}
return false;
}
public static bool IsMatch(int[] needle, int[] haystack, int x, int y, int needleWidth, int haystackWidth)
{
for (int i = 0; i < needle.Length; i++)
{
var innerX = i % needleWidth;
var innerY = i / needleWidth;
var cNeedle = needle[i];
var cHaystack = haystack[innerX + x + (innerY + y) * haystackWidth];
if (cNeedle != cHaystack) return false;
}
return true;
}
我已经修改了您的代码,以便您可以使用搜索缩进:
public static bool FindBitmap(Bitmap bmpNeedle, Bitmap bmpHaystack, out Point location, int startX, int startY)
{
if (bmpNeedle == null || bmpHaystack == null)
{
location = new Point();
return false;
}
for (int outerX = startX; outerX < bmpHaystack.Width - bmpNeedle.Width; outerX++)
{
for (int outerY = startY; outerY < bmpHaystack.Height - bmpNeedle.Height; outerY++)
{
for (int innerX = 0; innerX < bmpNeedle.Width; innerX++)
{
for (int innerY = 0; innerY < bmpNeedle.Height; innerY++)
{
Color cNeedle = bmpNeedle.GetPixel(innerX, innerY);
Color cHaystack = bmpHaystack.GetPixel(innerX + outerX, innerY + outerY);
if (cNeedle.R != cHaystack.R || cNeedle.G != cHaystack.G || cNeedle.B != cHaystack.B)
{
goto notFound;
}
}
}
location = new Point(outerX, outerY);
return true;
notFound:;
}
}
location = Point.Empty;
return false;
}