我在下面的源代码中结合了denfromufa和{a2}提出的解决方案,并做了一些全面的清理,这样您就可以看到您的代码是怎样的了。您还将注意到可读性的小改进,因为我使用C\7.0/.NET4.7编写了重构代码。在
实算法优化
虽然denfromula正确地指出了实现问题,并且HouseCat提到了使用更多的CPU资源,但真正的收益取决于减少图像搜索算法中执行的操作数。在TURBO STAGE 1-假设MinMax()函数遍历图像的所有像素来收集所有这些统计信息,但是您只对使用maxValue[0]感兴趣。一个极端的微调是编写一个特定的函数,当maxValue[0]低于最小阈值时,该函数将停止遍历图像的所有像素。显然,这就是你的功能所需要的。请记住:千万不要烧掉所有处理器,计算大量未使用的图像统计信息。
TURBO STAGE 2-看起来您正在尝试识别图像集的任何图像是否与您的输入屏幕截图(tela)匹配。如果没有太多的图像需要匹配,并且您经常检查屏幕上是否有新的匹配项,强烈建议您预加载所有这些图像匹配对象,并在函数调用中重用它们。恒定的磁盘IO操作和实例化位图类(对于每个屏幕截图)都会导致严重的性能损失。
TURBO STAGE 3-以防每秒拍摄多个屏幕截图,然后尝试重用屏幕截图的缓冲区。当屏幕截图的尺寸没有改变时,不断地重新分配整个屏幕快照的缓冲区也会导致性能损失。
涡轮4级-这是很难得到的,取决于你想为此投资多少。把你的图像识别系统看作一个大管道。位图作为在后台文件之间流动的数据容器(图像匹配阶段、OCR阶段、鼠标位置绘制阶段、视频录制阶段等)。我们的想法是创建一个固定数量的容器并重用它们,避免它们的创建和销毁。容器的数量类似于管道系统的“缓冲区大小”。当您的管道的几个阶段使用完这些容器后,它们将返回到管道的起点,即一种容器池。
最后一个优化使用这些外部库是很难实现的,因为在大多数情况下,它们的API需要一些内部位图实例化,而微调也会导致库和外部库之间的极端软件耦合。因此,您必须深入研究这些漂亮的库,以了解它们的实际工作原理,并构建您自己的自定义框架。我可以说这是一次很好的学习经历。在
这些库在很多方面都很酷;它们提供了一个通用的API来提高功能的可重用性。这也意味着在一个API调用中,它们处理的内容比实际需要的多得多。当涉及到高性能算法时,您应该始终重新考虑实现目标所需的这些库的基本功能是什么,如果它们是您的瓶颈,请自己动手。在
我可以说,一个好的微调图像识别算法不需要超过几毫秒的时间来完成您想要的。我曾体验过图像识别应用程序,它几乎可以在瞬间完成大屏幕截图(例如Eggplant Functional)。在
现在回到您的代码…
重构后的代码应该如下所示。我没有包括所有这些微调算法我已经提到了——你最好在SO里分别问他们问题。在Image source = new Image(ofd.FileName);
// Preferably use Path.Combine here:
string dir = Path.Combine(Directory.GetCurrentDirectory(), "Images");
// Check whether directory exists:
if (!Directory.Exists(dir))
throw new Exception($"Directory was not found: '{dir}'");
// It looks like you just need filenames here...
// Simple parallel foreach suggested by HouseCat (in 2.):
Parallel.ForEach(Directory.GetFiles(dir), (fname) =>
{
Image result = source.MatchTemplate(
new Image(fname.FullName),
Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);
// By using C# 7.0, we can do inline out declarations here:
result.MinMax(
out double[] minValues,
out double[] maxValues,
out Point[] minLocations,
out Point[] maxLocations);
if (maxValues[0] > 0.96)
{
// ...
var result = ...
return result; // <<< As suggested by: denfromufa
}
// ...
});
调音愉快;—)