我已经在Python中编写了一个解决方案,该解决方案效果很好,但是需要安装几个库并且需要很多专业设置。我决定在Visual Studio Community 2017上使用C#中的GUI构建它,但在第一个成功的函数中,结果比Python慢。哪个IMO实际上应该更快。
代码本质上只是在大海捞针图像搜索中,通过从文件夹中获取所有图像并在大海捞针中测试每个针(总共60个图像),在python中我返回字符串,但在C#I&#39中;我只打印。
我在Python中的代码如下:
def getImages(tela):
retorno = []
folder = 'Images'
img_rgb = cv2.imread(tela)
for filename in os.listdir(folder):
template = cv2.imread(os.path.join(folder,filename))
w, h = template.shape[:-1]
res = cv2.matchTemplate(img_rgb, template, cv2.TM_CCOEFF_NORMED)
threshold = .96
loc = np.where(res >= threshold)
if loc[0]>0:
retorno.append(filename[0]+filename[1].lower())
if len(retorno)> 1:
return retorno
并在C#中:
Debug.WriteLine(ofd.FileName);
Image<Bgr, byte> source = new Image<Bgr, byte>(ofd.FileName);
string filepath = Directory.GetCurrentDirectory().ToString()+"\\Images";
DirectoryInfo d = new DirectoryInfo(filepath);
var files = d.GetFiles();
foreach (var fname in files){
Image<Bgr, byte> template = new Image<Bgr, byte>(fname.FullName);
Image<Gray, float> result = source.MatchTemplate(template, Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);
double[] minValues, maxValues;
Point[] minLocations, maxLocations;
result.MinMax(out minValues, out maxValues, out minLocations, out maxLocations);
if (maxValues[0] > 0.96) {
Debug.WriteLine(fname);
}
}
我没有测量每个时间之间的时间,但我可以说C#中的结果大约需要3秒,而Python中的结果大约需要100毫秒。
有优化的空间,如果有人想建议任何改进,欢迎他们。
答案 0 :(得分:8)
问题是,在Python代码中,当至少有一个匹配添加到retorno
时,您就完成了迭代:
if len(retorno)> 1:
return retorno
在C#示例中,您将继续迭代,直到所有文件都循环完成。
答案 1 :(得分:2)
这个(denfromufa's answer)确实解释了你的问题,但也要捎带并添加一些建议/优化:
1。)您的GetFiles
可以替换为并行文件枚举器,也可以与子目录一起递归。我在GitHub上无耻地写了一些。
2。)你可以将foreach循环分成Parallel.ForEach(files, fname () => { Code(); });
再次,我在GitHub上的FileSearchBenchmark存储库中有大量的文件代码并行执行以提供examples。
答案 2 :(得分:2)
我在下面的源代码中结合了denfromufa和HouseCat提出的解决方案,并进行了一些整体清理,因此您可以看到您的代码是如何实现的。您还会注意到可读性的轻微改进,因为我使用 C#7.0 / .NET 4.7 编写了重构代码。
真实算法优化
虽然 denfromula 正确地指出实施问题,而 HouseCat 提到使用更多的CPU资源,但真正的增益依赖于减少图像搜索算法中执行的操作数量
TURBO STAGE 1 - 假设MinMax()
函数遍历所有图片的像素以收集所有这些统计信息,但您只对使用{{{{}}感兴趣1}}。极端微调将是编写一个特定的函数,当maxValue[0]
低于最小阈值时,该函数会停止迭代所有图像的像素。显然,这就是你在功能中所需要的一切。请记住:从不刻录所有处理器,计算大量未使用的图像统计信息。
TURBO STAGE 2 - 看起来您正在尝试识别您的图片集中的任何图片是否与您输入的屏幕截图(maxValue[0]
)匹配。如果没有太多图像要匹配,并且您经常检查屏幕是否有新匹配,则强烈建议您预先加载所有这些图像匹配对象,并在函数调用中重复使用它们。 持续磁盘IO操作和实例化位图类(针对每个屏幕截图)会导致性能受到强烈影响。
TURBO STAGE 3 - 如果您每秒拍摄多张屏幕截图,请尝试重复使用屏幕截图的缓冲区。当其尺寸根本没有改变时,不断重新分配整个屏幕截图的缓冲区也会导致性能下降。
TURBO STAGE 4 - 这很难搞定,取决于你想投入多少钱。 将您的图像识别系统视为一个重要的管道。位图作为阶段之间流动的数据容器(图像匹配阶段,OCR阶段,鼠标位置绘制阶段,视频录制阶段等)。我们的想法是创建固定数量的容器并重用它们,避免它们的产生和破坏。容器的数量就像&#34;缓冲区大小&#34;为您的管道系统。 当您的管道的几个阶段使用这些容器完成后,它们将返回到您的管道的开头,到达一种容器池。
使用这些外部库很难实现最后一次优化,因为在大多数情况下,它们的API需要一些内部位图实例化,并且微调还会导致库与外部库之间的极端软件耦合。所以你必须深入了解这些漂亮的库,以了解它们的实际工作方式,并构建自己的自定义框架。我可以说这是一次很好的学习经历。
这些图书馆很多用途很酷;它们提供了通用API,以提高功能的可重用性。这也意味着它们可以解决比单个API调用中实际需要的更多的东西。在高性能算法方面,您应该始终重新思考这些库实现目标所需的基本功能,如果它们是您的瓶颈,请自行完成。
我可以说,一个好的微调图像识别算法不需要花费几毫秒来完成你想要的事情。我经验丰富的图像识别应用程序几乎可以立即用于更大的屏幕截图(例如Eggplant Functional)。
现在回到您的代码......
您重构的代码应如下所示。我没有包括我提到过的那些微调算法 - 你最好在SO中为它们提出不同的问题。
tela
快乐调整; - )