Я объединил решения, предложенные denfromufa и HouseCat в приведенном ниже исходном коде, и провел некоторую общую очистку, чтобы вы могли увидеть, каким может быть ваш код. Вы также заметите небольшие улучшения читабельности, поскольку я написал переработанный код, используя C # 7.0 / .NET 4.7 .
Реальная оптимизация алгоритма
Хотя denfromula правильно указал на эту проблему реализации, а HouseCat упомянул использование большего количества ресурсов ЦП, истинное усиление зависит от уменьшения количества операций, выполняемых во время алгоритма поиска изображений.
TURBO STAGE 1 - Предположим, что функция MinMax()
просматривает все пиксели вашего изображения, чтобы собрать всю эту статистику, но вас интересует только использование maxValue[0]
. Чрезвычайно тонкой настройкой было бы написать определенную функцию, которая прекращает повторение всех пикселей вашего изображения , когда maxValue[0]
опускается ниже вашего минимального порога. Видимо, это все, что вам нужно в вашей функции. Помните: никогда не сжигает все ваши процессоры, вычисляя большое количество неиспользуемой статистики изображений .
TURBO STAGE 2 - Похоже, вы пытаетесь распознать, соответствует ли какое-либо изображение из вашего набора изображений вашему входному скриншоту (tela
). Если не требуется слишком много изображений для сопоставления и если вы постоянно проверяете свой экран на наличие новых совпадений, настоятельно рекомендуется предварительно загрузить все эти объекты сопоставления изображений и повторно использовать их в вызовах функций. Постоянные операции дискового ввода-вывода и создание экземпляров классов растровых изображений (для каждого отдельного снимка экрана) приводят к сильному снижению производительности.
TURBO STAGE 3 - На всякий случай, если вы делаете несколько снимков экрана в секунду, затем попробуйте повторно использовать буфер снимка экрана. Постоянное перераспределение всего буфера скриншота, когда его размеры просто не изменились, также приводит к снижению производительности .
ТУРБО ЭТАП 4 - Это трудно получить, и зависит от того, сколько вы хотите вложить в это. Думайте о своей системе распознавания изображений как о большом конвейере. Растровые изображения как контейнеры данных, проходящих между вашими этапами (этап сопоставления изображений, этап распознавания, этап рисования положения мыши, этап видеозаписи и т. Д.). Идея состоит в том, чтобы создать фиксированное количество контейнеров и использовать их повторно, избегая их создания и уничтожения. Количество контейнеров похоже на «размер буфера» для вашей конвейерной системы. Когда несколько этапов вашего конвейера закончили использовать эти контейнеры, они возвращаются в начало вашего конвейера, в своего рода пул контейнеров.
Эту последнюю оптимизацию действительно трудно достичь с помощью этих внешних библиотек, поскольку в большинстве случаев их API требует некоторой внутренней реализации растрового изображения, а точная настройка также приведет к чрезмерной программной связи между вашей библиотекой и внешней. Так что вам придется копаться в этих замечательных библиотеках, чтобы понять, как они на самом деле работают, и создать свой собственный Framework. Я могу сказать, что это хороший опыт обучения.
Эти библиотеки действительно хороши для многих целей; они предоставляют общий API для улучшенного повторного использования функциональности. Это также означает, что они решают гораздо больше задач, чем нужно на самом деле в одном вызове API. Когда дело доходит до высокопроизводительных алгоритмов, вы должны всегда переосмысливать, какие основные функции вам нужны от этих библиотек для достижения вашей цели, и если они являются вашим узким местом, сделайте это самостоятельно.
Могу сказать, что хороший отлаженный алгоритм распознавания изображений занимает не более нескольких миллисекунд, чтобы сделать то, что вы хотите. У меня были приложения для распознавания изображений, которые делают это практически мгновенно для больших скриншотов (например, Функциональность баклажана ).
Теперь вернемся к вашему коду ...
Ваш реорганизованный код должен выглядеть следующим образом. Я не включил все те тонко настроенные алгоритмы, о которых упоминал, - вам лучше задать для них отдельные вопросы в SO.
Image<Bgr, byte> source = new Image<Bgr, byte>(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<Gray, float> result = source.MatchTemplate(
new Image<Bgr, byte>(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
}
// ...
});
Happy Tuning; -)