Представленный модуль graph.lua содержит пакет альтернативных функций:
GetImage
SaveImage
LoadImage
FindColor
FindImage

    Имена альтернативных функций отличаются от  стандартных  ПРОПИСНЫМИ  символами.  Вы  можете  использовать  как
стандартные, так и альтернативные функции в одном скрипте одновременно. НО, необходимо  помнить,  что  стандартные
функции несовместимы с альтернативными. Иначе говоря, если Вы использовали например для загрузки образа  в  память
функцию LoadImage, то и поиск осуществлять Вы должны функциями FindColor и FindImage, а не стандартными  findcolor
и findimage.
    Для доступа к функциям модуля поместите его в любое удобное для Вас место, и в начале своих скриптов  вставьте
соответствующую строку, определяющую местонахождение модуля. В приводимых ниже примерах предполагается, что модуль
расположен в папке LUA\, созданной в папке Пилота Scripts\ и соответственно строка доступа к модулю выглядит так:
  require[[Scripts\LUA\graph]]

** Текст начинающийся со знаков звёздочек содержит технические детали.

Плюсы и минусы.
Минусы
  1) Нет обработки ошибок (делал исключительно для себя). Поэтому использовать функции надо аккуратно  и  задавать
     корректные координаты. В противном случае возможен даже вылет Пилота, так как  выполняются  операции напрямую
     модифицирующие память.
  2) Отсутствие совместимости со стандартными функциями,  в  том  числе  и  синтаксическая  совместимость.  Замену
     стандартных функций на альтернативные можно  делать  по  мере  необходимости,  там  где  нужна  скорость  или
     расширенный функционал поиска.
Плюсы
  1) Достоверность поиска.
  2) Расширенные возможности поиска.
  3) Скорость работы (в подавляющем большинстве случаев).

    Модуль при старте скрипта создаёт глобальную переменную CI - это lua таблица,  содержащая  СИ-массивы  данных,
в которые сохраняются результаты работы функций и доступ к которым  может  выполняться  из  любого  места  скрипта
пользователя. Массив CI[0] содержит результаты работы функции FindColor.  Массивы  CI[1]  и  CI[2]  -  это массивы
данных,  содержащие  битовые  маски  загружаемых  в  память  изображений.  По  умолчанию  количество  одновременно
используемых изображений равно ДВУМ, а размер  СИ-массивов  соответствует  размеру  максимально возможного размера
образа в памяти, равного 1920 х 1080. Вы можете изменить этот параметр, отредактировав  соответствующую  строку  в
самом начале модуля, а также при необходимости увеличить количество доступных битовых масок изображений.

**     Концепция. Использование глобальной таблицы конечно же имеет свои  очевидные  минусы,  в  частности  жестко
** фиксированное количество используемых для одновременной  работы  изображений  и  заранее  фиксированный  размер
** СИ-массивов. Однако есть и очевидное преимущество такого подхода -  это  скорость  работы  функций.  Выигрыш  в
** скорости обусловлен 2-мя факторами:
**   1) Не тратится время на выделение памяти под СИ-массивы (особенно  это  актуально  при  осуществлении  поиска
**      функцией FindColor по максимальному  возможному  изображению  1920 х 1080,  поскольку  при  каждом  вызове
**      функции под результирующий массив данных выделялось бы 20 736 000 байт);
**   2) Прямой доступ к данным при осуществлении поиска, минуя вызовы ffi.cast (особенно это актуально при  поиске
**      картинок)


ОПИСАНИЕ функций.
------------------------------------------------------------------------------------------------------------------
--   Функция GetImage                                                                                           --
------------------------------------------------------------------------------------------------------------------
GetImage( x1, y1, x2, y2 [,numBitMap [,handle] ] )
  x1, y1, x2, y2 - координаты зоны сканирования
  numBitMap      - номер битовой маски для сохранения образа (1 или 2), по умолчанию = 1.
  handle         - хендл окна сканирования, по умолчанию рабочее окно

  Параметр numBitMap - это логический аналог параметра адреса в стандартной функции.
  Если параметр handle не задан, а хендл рабочего окна не определён ИЛИ handle равен нулю - выполняется
    сканирование экрана по абсолютным координатам (аналог стандартного abs).

  Примеры использования:
    GetImage( 0, 0, 99, 99 )            - сканирование рабочего окна - изображение сохраняется в массив № 1.
    GetImage( 0, 0, 99, 99, 2 )         - сканирование рабочего окна - изображение сохраняется в массив № 2.
    GetImage( 0, 0, 99, 99, 1, 0)       - сканирование экрана - изображение сохраняется в массив № 1.
    GetImage( 2000, 0, 2099, 99, 1, 0)  - сканирование экрана (зона 2-го монитора) - изображение сохраняется в
                                          массив № 1.

    Если требуется сканировать зону размером более чем в  2 073 600  пикселей  (1920 * 1080),  увеличте  параметры
maxWidth и (или) maxHeight в 4-ой строке модуля.

** Первые 4 байта массива битовой маски являются служебными и хранят параметры ширины и высоты изображения.


------------------------------------------------------------------------------------------------------------------
--   Функция SaveImage                                                                                          --
------------------------------------------------------------------------------------------------------------------
SaveImage(path [,numBitMap [,x1, y1, x2, y2] ] )
  path           - полное имя файла для сохранения изображения.
  numBitMap      - номер битовой маски для сохранения образа в файл (1 или 2), по умолчанию = 1.
  x1, y1, x2, y2 - координаты области, которую требуется сохранить (если требуется сохранить часть образа).

  Примеры использования:
    GetImage( 0, 0, 99, 99 )            - сканирование области рабочего окна - изображение сохранено в массив № 1.
    SaveImage(C:\temp.bmp)              - сохранение картинки в файл, образ которой хранится в массиве № 1.

      Пример, приведённый выше, самый простой и надёжный способ определить происходит сканирование окна или нет  и
      определить какая именно зона сканируется (нулевые координаты), чтобы определить смещения  для  кликов  мыши.
      Сделайте привязку к рабочему окну и выполните эти 2 оператора.

    GetImage( 0, 0, 99, 99, 2 )         - сканирование области рабочего окна - изображение сохранено в массив № 2.
    SaveImage(C:\temp.bmp, 2)                  - сохранение картинки в файл, хранящейся в массиве № 2.
    SaveImage(C:\temp.bmp, 2, 20, 40, 89, 89)  - сохранение части картинки в файл, хранящейся в массиве № 2,
      сохраняется только область с заданными координатами.
      ВНИМАНИЕ. Координаты x2 и y2 не должны быть больше тех, что были использованы при вызове GetImage.


------------------------------------------------------------------------------------------------------------------
--   Функция LoadImage                                                                                          --
------------------------------------------------------------------------------------------------------------------
LoadImage(path [,numBitMap] )
  path           - полное имя файла из которого загружается изображение.
  numBitMap      - номер битовой маски для загрузки образа (1 или 2), по умолчанию = 1.

  Получить параметры изображения можно вызвав специальную функцию (также доступна в данном модуле)
    length, width, height = GetBMI(numBitMap)
      где length - длина строки пикселей образа; width, height - ширина и высота изображения в пикселях.


------------------------------------------------------------------------------------------------------------------
--   Функция FindColor                                                                                          --
------------------------------------------------------------------------------------------------------------------
bool, numFind = FindColor( x1, y1 [,x2,y2] , {condition} )   - ОЧЕНЬ быстрая функция, можно "лопатить" что угодно.
  bool             - результат поиска - ИСТИНА, если пиксели (пиксель) найдены, ЛОЖЬ в противном случае.
  numFind          - количество найденных пикселей, -1 если ничего не найдено, 0 - найден 1 пиксель и так далее.

  x1, y1 [,x2,y2]  - координаты области поиска, если указаны только 2 координаты - проверка только одного пикселя.
  {condition}      - условие поиска (таблица ключей). Условие поиска ОДНО. В своей практике не находил случая,
                     когда требовалось несколько условий. В случае необходимости ничто не мешает выполнять
                     из основного скрипта столько поисков сколько нужно, скорость от этого не пострадает.
    ключи таблицы условия поиска:
      hW           - хендл окна поиска (если = 0 - поиск по абсолютным координатам), если он не определён, поиск
                     выполняется по РАНЕЕ созданной битовой маске, по умолчанию по маске с № 1 (смотри ключ ниже).
      bm           - номер битовой маски по которой будет осуществляться поиск, по умолчанию = 1.
                     если задан хендл окна поиска, то в данную битовую маску будет сначала помещено изображение
                     указанной области окна, по которому и будет осуществляться поиск.
      nP           - количество искомых пикселей, по умолчанию = 1. Отрицательное значение - искать все возможные.
      stX          - шаг поиска по шкале X, по умолчанию = 1.
      stY          - шаг поиска по шкале Y, по умолчанию = 1.
      r     - минимальное значение канала RED искомых пикселей (0 - 255)
      R     - максимальное значение канала RED искомых пикселей, по умолчанию равно минимальному r,
              не имеет силы если не задан ключ r
      g     - минимальное значение канала GREEN искомых пикселей
      G     - максимальное значение канала GREEN искомых пикселей, по умолчанию равно минимальному g
              не имеет силы если не задан ключ g
      b     - минимальное значение канала BLUE искомых пикселей
      B     - максимальное значение канала BLUE искомых пикселей, по умолчанию равно минимальному b
              не имеет силы если не задан ключ b
      rg    - минимальная разность между каналами RED-GREEN (искомых пикселей) (-255 - 255)
      RG    - максимальная разность между каналами RED-GREEN, по умолчанию 255
              не имеет силы если не задан ключ rg
      rb    - минимальная разность между каналами RED-BLUE (-255 - 255)
      RB    - максимальная разность между каналами RED-BLUE, по умолчанию 255
              не имеет силы если не задан ключ rb
      gb    - минимальная разность между каналами GREEN-BLUE (-255 - 255)
      GB    - максимальная разность между каналами GREEN-BLUE, по умолчанию 255
              не имеет силы если не задан ключ gb

  Реальные (далее начинающиеся с --lua реально работающие) примеры использования:

--lua
require[[Scripts\LUA\graph]]                      -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
  --\/ Если в указанной координате рабочего окна найден пиксель у которого канал RED = от 0 до 255 (любой пиксель) ...
  --\/ Если привязки к рабочему окну нет, будет выполнено сканирование по абсолютным координатам.
if FindColor( 7, 2, {hW=workwindow(), bm=2, r=0, R=255} ) then
    log('X=',CI[0][0][0])           -- Вывод абсциссы найденного пикселя, равна 0, т.к. координата относительная,
                                    -- реальная в окне = +7
    log('Y=',CI[0][0][1])           -- Вывод ординаты найденного пикселя, равна 0
    log('Канал BLUE=',CI[0][0][2])  -- Вывод значения канала BLUE найденного пикселя ...
    log('Канал GREEN=',CI[0][0][3])
    log('Канал RED=',CI[0][0][4])
end
  --/\ Область поиска рабочего окна будет отсканирована в битовую маску № 2 (всего один пиксель), которую
  --/\ можно использовать в дальнейшем (например сохранить в файл даже один пиксель).
  --/\ CI[0][0] - это массив данных (параметров) ПЕРВОГО найденного пикселя; CI[0][1] - второго пикселя и т.д.

-------------------------

        ВНИМАНИЕ. Далее будут представлены примеры, для работы которых требуются картинки  из  архива.  Для  этого
    поместите файлы картинок архива в отдельную папку и проинициализируйте к ней путь переменной path.
    По умолчанию используется папка хранения:  local path = [[A:\find\]]

--lua
local path = [[A:\find\]]                         -- !!!!! Укажите СВОЮ папку хранения файлов BMP
require[[Scripts\LUA\graph]]                      -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
LoadImage(path..[[Empire.bmp]])                   -- Загрузка изображения из файла в битовую маску №1 (по умолчанию)
  --\/ Поиск первых 8 "бесцветных" пикселей, в указанных координатах (по умолчанию в битовой маске №1)
local b,k = FindColor (0, 0, 900, 900, {rg=-20, RG=20, rb=-20, RB=20, gb=-20, GB=20, nP=8})
if b then                                         -- Если пиксели были найдены (можно задать условие и так if k>=0)
    log('Найдено ',k+1,' пикселей') -- Плюсуем 1, т.к. индексация найденных пикселей начинается с нуля.
    log('X=',CI[0][k][0])           -- Вывод абсциссы ПОСЛЕДНЕГО найденного пикселя
    log('Y=',CI[0][k][1])           -- Вывод ординаты ПОСЛЕДНЕГО найденного пикселя
    log('Канал BLUE=',CI[0][k][2])  -- Вывод значения канала BLUE ПОСЛЕДНЕГО найденного пикселя ...
    log('Канал GREEN=',CI[0][k][3])
    log('Канал RED=',CI[0][k][4])
end ------------------------------------------------------------------------------

        Выше приведён пример использования ключей поиска по разности  каналов.  Это  просто  пример  использования
    ключей. Конечно их можно применять для разных вариантов поиска. И для конкретных случаев можно  варьировать  и
    степень бесцветности и диапазон каналов RGB, например:
    {r=200, R=255, rg=-40, RG=40, rb=-40, RB=40, gb=-40, GB=40}   -- будут искаться "светлые" "бесцветные" пиксели
    {rg=25, rb=25} -- будет искаться ЛЮБОЙ оттенок красного от самого яркого, до самого бледного.

        Из примеров видно, что результат поиска всегда сохраняется в массив CI[0].  Параметры  первого  найденного
    пикселя помещаются в массив CI[0][0], второго в массив CI[0][1], третьего в CI[0][2] и т.д.  При каждом  новом
    вызове функции FindColor массив CI[0] обновляется автоматически, поэтому если данные поиска будут востребованы
    позже, необходимо их сохранить в локальный массив данных, до следующего вызова FindColor.


------------------------------------------------------------------------------------------------------------------
--   Функция FindImage                                                                                          --
------------------------------------------------------------------------------------------------------------------
NumFindPic, ResultArray = FindImage ( x1, y1, x2, y2, {picture} [, {sourse} ] )
  numFindPic       - количество найденных картинок, 0 если ничего не найдено.
  ResultArray      - массив, содержащий координаты найденных картинок

  x1, y1, x2, y2   - координаты области поиска
  picture          - искомая картинка
  sourse           - источник поиска (где ищется картинка)

  Сначала разберёмся с источником sourse - это таблица ключей условий поиска.
      hW           - хендл окна поиска, если он не определён, поиск выполняется по ранее созданной битовой маске,
                     по умолчанию по маске с № 1 (смотри ключ ниже).
      bm           - номер битовой маски по которой будет осуществляться поиск, по умолчанию = 1.
                     если задан хендл окна поиска, то в данную битовую маску будет сначала помещено изображение
                     указанной области окна (экрана), по которому и будет осуществляться поиск.
      nP           - количество искомых картинок, по умолчанию = 1. Отрицательное значение - искать все возможные.
      shiX         - смещение поиска по шкале X, после очередной найденной картинки, по умолчанию = 1. 
      shiY         - смещение поиска по шкале Y, после очередной найденной картинки, по умолчанию = 1.
                       Далее будут даны примеры, поясняющие применение смещений, исключающие поиск дубликатов картинок.
      sim          - Параметр похожести искомых картинок с эталонной - аналог параметра accuracy (точность) стандартного финда.
                     Очень медленная работа, оптимизировать этот алгоритм мне не удалось, поэтому использовать его я
                     рекомендую в исключительных случаях, либо для таких случаев использовать стандартную функцию.
                       Далее будут даны примеры использования.
 **         Ниже описаны ключи приоритета поиска, лучше их не трогать без чёткого понимания что ищется.
 **         Изначально предполагалась зависимость скорости работы от их значений, на практике прирост
 **         в подавляющем большинстве случаев мизерный. Оставил их просто на всякий случай.
 **   v1           - первый (приоритетный) канал по которому начинается поиск, по умолчанию =2 - RED
 **   v2           - второй канал по которому продолжается поиск, по умолчанию =1 - GREEN
 **   v3           - последний канал по которому завершается поиск, по умолчанию =0 - BLUE
 **         Если изменять эти ключи, то в итоге должны быть заданы все ТРИ значения каналов (0,1,2),
 **         в противном случае можно найти что угодно.
  
  picture - искомая картинка. Это таблица содержащая 2 СИ-массива {<индексы искомых пикселей>, <массив искомых пикселей>}.
  Формирование искомой картинки может быть выполнено как заранее и использоваться потом неограниченное число  раз,
  так и непосредственно самой функцией FindImage. От того  насколько  "грамотно"  выполнено  формирование  искомой
  картинки, зависит достоверность и скорость её поиска, причём  разница  по  скорости  может  отличаться  в  разы.
  Формирование искомой картинки выполняет специальная функция:

    CreateFindArray( { {condition} }, LengthBM)
      condition  - это двухуровневая таблица ключей условий формирования массива искомых пикселей.
                   То есть количество условий неограничено.
      LengthBM   - Длина строки пикселей битовой маски по которой будет искаться картинка. Если указать неверное
                   значение, функцией FindImage будет автоматически выполнен перерасчёт индексов, это чуть-чуть
                   увеличит время поиска.
        
        ключи таблицы условий формирования массива искомых пикселей {condition}.
          nf    - Имя файла картинки. Если картинка уже загружена функцией LoadImage, данный ключ требуется пропустить,
                  в противном случае загрузка осуществится повторно.
          bm    - Номер битовой маски в которую будет загружаться картинка (или уже загружена), по умолчанию № 2
                  (единственный случай, когда по умолчанию используется вторая битовая маска)
          zat   - Процент затенения экрана при котором будет искаться картинка, по умолчанию 0%
              три вышеупомянутых ключа могут быть использованы (и будут иметь силу) только в первом условии
              (condition[1].nf  condition[1].bm  condition[1].zat)

              Ключи указанные ниже индивидуальны для каждого условия формирования массива искомых пикселей.
          r, R, g, G, b, B, rg, RG, rb, RB, gb, GB - описаны выше в функции FindColor
          acc   - точность поиска - относительное в % отклонение каналов RGB пикселей в области поиска
                  от пикселей эталонной картинки, по умолчанию =0
          dev   - погрешность поиска - абсолютное значение отклонения, по умолчанию =0,
                  это аналог стандартного deviation только не в %, а в абсолютной величине (от 0 до 255).
                  Ограничения нет, но глупо задавать его очень большим.
          fgr   - флаг определяющий каким образом формируется массив искомых (не фоновых) пикселей.
                  По умолчанию (если флаг не определён или = false) условие применяется для поиска фоновых пикселей,
                  соответственно пиксели не удовлетворяющие условию поиска будут считаться искомыми.
                  И наоборот, если fgr = true, искомыми будут пиксели удовлетворяющие заданному условию.
                    Примеры: {r=0, R=255, fgr=true} - все пиксели картинки будут искомыми;
                             {rg=20, rb=20, fgr=true} - все пиксели имеющие малейший красный оттенок будут искомыми;
                             {r=0} - искомыми будут все пиксели, канал RED у которых НЕ равен нулю.

  Обратите внимание, что параметры отклонений (accurace и deviation) задаются именно при формировании картинки,
  ДО осуществления поиска, в отличие от параметра похожести (similar), который используется уже самой функцией поиска.
  А также параметры отклонений индивидуальны для каждой группы (соответствующих условию) искомых пикселей картинки.
  Приоритет условий также очень важен и влияет на скорость поиска.
  Для наглядности подробно рассмотрим такое условие (условия):
  { {r=0, dev=5},
    {fgr=true, g=255, dev=10}
    {fgr=true, b=255, acc=20}
  }
  После выполнения первого условия искомыми будут все пиксели у которых канал RED не равен нулю.
  Второе условие будет дополнением к первому и искомыми будут считаться также и пиксели у которых
  канал RED равен нулю (в первом условии они были фоновыми) и канал GREEN = 255.
  Третье условие дополняет первое и второе условия и искомыми будут считаться также и пиксели у которых
  канал RED = 0  И  канал GREEN ~= 255  И  канал BLUE = 255.
  Каждая группа искомых пикселей соответственно имеет свои допустимые отклонения поиска.
  Порядок задаваемых условий формирования искомых пикселей определяет и приоритет поиска.
  Сначала будут искаться пиксели первой группы, затем второй группы, затем третьей.
  Поэтому желательно первым условием задавать группу пикселей, которые являются самыми редкими (уникальными).

  Логический смысл работы функции - она создаёт как бы "трафарет" по которому и осуществляется поиск.
  
=======================================================================================================================


    Далее представлены реальные рабочие примеры использования FindImage, смысл которых показать  методику  поиска,
которую я использую для поиска картинок. То есть вы можете непосредственно копировать и  выполнять  представленные
примеры кода (не забывайте правильно указать СВОЙ путь к каталогу хранения файлов картинок).

**    Сразу оговорюсь, примеры поиска символов рассматриваться не будут - это абсолютно  отдельная  тема  и  очень
** долгий разговор. Могу лишь сказать, что со сглаживанием шрифтов, можно легко справиться,  но  для  этого  лучше
** всего создавать минимум по ТРИ варианта картинок каждого символа. Я делаю это одним набором,  но  только  после
** анализа ТРЁХ полученных опытным путём. Вообще сей процесс можно автоматизировать, но это явно отдельная тема.

    Итак, начнём с поиска золотых монеток, на изображении в файле Empire.bmp. Чтобы была какая-то база, от которой
можно отталкиваться, выполним сначала поиск стандартной функцией. Сразу скажу, параметры взял с потолка  и  сильно
завысил, просто чтобы посмотреть что и как будет искаться.
    ВНИМАНИЕ! Будет не быстро, около 40 секунд, ничего не повисло - ищется!

--lua
local path = [[A:\find\]]                        -- !!!!! Укажите СВОЮ папку хранения файлов BMP
log 'clear' log 'mode compact'
local screen = loadimage (path..[[Empire.bmp]])  -- загрузка скрина экрана поиска
local pict = loadimage (path..[[gold.bmp]])      -- загрузка искомой картинки (вырезана с первой попавшейся картинки)
local accuracy = 80                              -- точность поиска (у меня это схожесть)
local count = -1                                 -- искать все картинки
local deviation = 30                             -- погрешность оттенка (абсолютная = 76 единицам = 30% от 255)
local time=os.clock()                            -- засечка времени
local arr = findimage (0, 0, 1919, 968, {pict}, screen, accuracy, count, deviation)
log ('Затрачено времени на поиск: ' .. tostring(os.clock() - time))
if arr then    -- если изображения найдены
    log('Найдено изображений: ' .. tostring(#arr) .. '\r\nКоординаты изображений:')
    for i = 1, #arr do
        log(i .. ' -', arr[i][1], arr[i][2], '\t% совпадения:', arr[i][5])
    end
end --------------------------------------------------------------------------------

ЛОГ информация результатов работы скрипта {это мои комментарии}:

Затрачено времени на поиск: 42.192000000039
Найдено изображений: 24
Координаты изображений:
1 - 910 255 	% совпадения: 100
2 - 978 367 	% совпадения: 80   {картинка №4 со звёздочкой}
3 - 1089 391 	% совпадения: 88
4 - 1090 391 	% совпадения: 82   {дубликат}
5 - 1339 422 	% совпадения: 89
6 - 1339 423 	% совпадения: 80   {дубликат}
7 - 1183 438 	% совпадения: 97
8 - 1120 469 	% совпадения: 84
9 - 1121 469 	% совпадения: 89   {дубликат}
10 - 1245 469 	% совпадения: 88
11 - 1246 469 	% совпадения: 81   {дубликат}
12 - 1183 500 	% совпадения: 88
13 - 590 516 	% совпадения: 97
14 - 1027 516 	% совпадения: 97
15 - 528 547 	% совпадения: 96
16 - 652 547 	% совпадения: 83
17 - 653 547 	% совпадения: 87   {дубликат}
18 - 964 547 	% совпадения: 83
19 - 965 547 	% совпадения: 87   {дубликат}
20 - 1089 547 	% совпадения: 88
21 - 1090 547 	% совпадения: 81   {дубликат}
22 - 1027 578 	% совпадения: 88
23 - 871 594 	% совпадения: 97
24 - 996 625 	% совпадения: 96

Резюме.
  Радует что найдена картинка со звёздочкой № 4 (в логе №2).
  Неудивительно, что другие со звёздочкой не были найдены (№№ 2,3,6,7)
  УДИВИТЕЛЬНО, но не найдена последняя картинка № 22
  И найдено 7 дубликатов картинок, отличающихся координатами на единичку.

  Повторим поиск при аналогичных параметрах поиска альтернативной функцией FindImage

--lua
local path = [[A:\find\]]                      -- !!!!! Укажите СВОЮ папку хранения файлов BMP
require[[Scripts\LUA\graph]]                   -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
LoadImage (path..[[Empire.bmp]])
LoadImage (path..[[gold.bmp]],2)
local L,w,h=GetBMI(1)                          -- Получение параметров изображения экрана (битовой маски №1)
--\/ Формирование эталонной картинки для поиска.
--   В качестве фона будем также считать левый верхний пиксель картинки gold.bmp
--   Его RGB=(110,117,41). Параметр погрешность поиска (абсолютная величина) такая же = 76 единиц
--   L - длина строки пикселей картинки экрана поиска (Empire.bmp)
local gold=CreateFindArray({ {bm=2, r=110, g=117, b=41, dev=76} }, L)
local time=os.clock()
--\/ Поиск в битовой маске №1 (по умолчанию) картинки (gold),
--   ищем все картинки (nP=-1), похожесть 80%
local k, m = FindImage( 0, 0, w-1, h-1, gold, {nP=-1, sim=80} )
log ('Затрачено времени на поиск: ' .. tostring(os.clock() - time))
if k>0 then
    log(k)
    for i=1,math.min(k,30)do  -- вывод информации (не более чем для 30 картинок)
        log(m[i][1],m[i][2])
    end
  else  log("картинки не найдены")
end --------------------------------------------------------------------------------

ЛОГ информация результатов работы скрипта {это мои комментарии}:

Затрачено времени на поиск: 9.6900000000605
Найдено изображений: 27
Координаты изображений:
910 255
978 367
1089 391
1090 391   {дубликат}
1339 422
1339 423   {дубликат}
1183 438
1120 469
1121 469   {дубликат}
1245 469
1246 469   {дубликат}
1183 500
1183 501   {дубликат}
590 516
1027 516
528 547
652 547
653 547    {дубликат}
964 547
965 547    {дубликат}
1089 547
1090 547   {дубликат}
1027 578
1027 579   {дубликат}
871 594
996 625
902 641

Резюме.
      Найдено 9 (на 2 больше) дубликатов картинок. И найдена последняя картинка  № 22,  не  найденная  стандартной
  функцией поиска.
Вывод.
      Для полной уверенности, вдруг что-то не так округлялось в параметрах поиска  я  повторил  поиск  стандартной
  функцией с параметрами accuracy = 79 и deviation = 31. В результате нашлось 27 изображений, НО картинка № 22 так
  и не была найдена, а нашлось больше дубликатов.  Достоверность  работы  стандартной  функции  вызывает сомнение.
  Факты  некорректной работы стандартной функции поиска мною выявлялись не раз, разбираться в тонкостях я не стал,
  поэтому больше никаких сравнений и аналогий со стандартной функцией не будет.


Борьба с дубликатами:
Надо просто заменить 16 строку вышеприведённого скрипта:
  local k, m = FindImage( 0, 0, w-1, h-1, gold, {nP=-1, sim=80} )
На такую, используя ключи shiX=1, shiY=1
  local k, m = FindImage( 0, 0, w-1, h-1, gold, {nP=-1, sim=80, shiX=1, shiY=1} )

    Будет найдено уже только 18 картинок (без дубликатов). Стоит отметить, что  по  оси  X  смещение  можно  смело
ставить хоть 50 (ближайшая картинка от найденной будет дальше), а по оси Y до 3-х пикселей точно.


    Промежуточный итог. Если совсем лень работать с картинками в редакторе и скорость поиска  не  сильно  волнует,
можно остановиться на представленном варианте  поиска  и  даже  не  заморачиваться  по  поводу  фоновых  пикселей.
Достаточно нарезать 2 (2-ая со звёздочкой) картинки и просто формировать массив искомых пикселей из ВСЕХ  пикселей
картинки:

local gold=CreateFindArray({{bm=2, r=0, R=255, fgr=true, dev=76}},L)
--/\ ключ fgr=true означает что условие задано не для поиска пикселей фона, а для поиска искомых (значимых) пикселей
-- (сорри за тафту), то есть r=0, R=255 - означает что все пиксели должны быть искомыми,
-- результат поиска будет прежним, так как добавится к искомым только один пиксель (левый верхний).


    ОДНАКО. Достоверность поиска при таком подходе оставляет желать лучшего. Что там на искомом экране в следующий
момент времени может "нарендериться" предсказать сложно. И смысл новой функции как раз в том  и  заключается,  что
можно задавать любые комбинации искомых пикселей. В качестве искомых надо использовать  "уникальные",  характерные
только для искомой картинки пиксели (точнее комбинации их взаимного расположения друг относительно друга).
    На примере поиска золотых монеток будет подробно рассмотрена такая методика поиска.
    Давайте повнимательней поглядим  на  экран  поиска  и  искомую  картинку.  У  монеток  "уникальными"  являются
ярко-жёлтые пиксели, следовательно именно эти пиксели и должны быть критерием поиска.
    Итак, правим исходную картинку. Как лучше править? Наиболее подвержены рендерингу (сильному искажению) границы
объектов. Для картинок небольшого размера (как в нашем случае)  область  наибольшего  рендеринга  границ  объектов
составляет примерно 2 пикселя. Поэтому искомыми должны быть пиксели, отстоящие от границ  объектов  минимум  на  2
пикселя, то есть в поиске должны участвовать только пиксели "стабильной" зоны. В нашем примере наименее подвержены
искажению ярко-жёлтые пиксели в центре верхней  монетки.  Ими  и  формируем  новую  картинку  поиска  goldF1.bmp .
Остальные пиксели просто "красим" чёрным цветом RGB(0,0,0) - они будут фоном.

    Приведённый ниже пример - это не просто поиск картинок, а определение граничных значений  параметра  точности,
при котором корректно ищутся все 22 картинки (включая изображения со  звёздочкой).  Предпочтительнее  использовать
при поиске светлых пикселей именно параметр точности (acc), а не погрешности (dev).  Несмотря  на  то,  что  поиск
картинок будет выполняться в цикле многократно, да ещё и с формированием искомой картинки, всё отработает быстро.
    Далее поиск осуществляется начиная с ординаты 30 (чтобы не искалась картинка в строке статуса).

--lua
local path = [[A:\find\]]                          -- !!!!! Укажите СВОЮ папку хранения файлов BMP
require[[Scripts\LUA\graph]]                       -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
LoadImage (path..[[Empire.bmp]])                   -- Загрузка экрана поиска (битовая маска №1)
LoadImage (path..[[goldF1.bmp]],2)                 -- Загрузка картинки поиска (битовая маска №2)
local L,w,h=GetBMI(1)                              -- Получение параметров изображения экрана (битовой маски №1)
local NotFirstFind = true                          -- установка флага признака что картинки пока не найдены
for i = 100, 0, -1 do                              -- цикл поиска граничных допустимых значений параметра "точность"
  --\/ поиск картинок с текущим значением параметра точность равным индексу цикла
  --\/ формирование искомой картинки (по битовой маске №2) выполняется непосредственно самой функцией поиска
  --\/ Фоновыми будут чёрные пиксели (r=0).
  local k, m = FindImage( 0, 30, w-1, h-1, { {r=0, acc=i} },   {nP=-1, shiX=20, shiY=3} )
  if NotFirstFind  and  k >= 22 then                 -- Если все картинки ещё не найдены  И  текущее количество найденных не менее 22 (всех искомых)
    if k == 22 then                                    -- Если найдено картинок столько, сколько нужно
        log("Максимально допустимая точность =",i)         -- Вывод максимально допустимого значения параметра точности
        NotFirstFind = false                               -- картинки найдены, сброс флага признака что картинки пока не найдены
      else                                               -- Иначе, картинок найдено больше имеющихся
        log("Ошибка - Картинки не найдены")                -- Вывод в лог об ошибке
        break                                              -- Прерывание цикла
    end
  end
  if k>22 then                                       -- Если количество найденных картинок больше искомых (ищется не то, что надо)
    log("Минимальный уровень точности =",i+1)          -- Вывод минимально допустимого значения параметра точности (предыдущее значение индекса цикла)
    break                                              -- Прерывание цикла
  end
end --------------------------------------------------------------------------------

ЛОГ информация результатов работы скрипта:

Максимально допустимая точность = 74
Минимальный уровень точности = 72

    Результаты неутешительные. Несмотря на то, что картинки  нашлись,  диапазон  варьирования  параметра  точность
ничтожно мал. Попробуем проверить работу параметра погрешность (dev - отклонение каналов в абсолютных  величинах).
Немного изменим скрипт.

--lua
local path = [[A:\find\]]                          -- !!!!! Укажите СВОЮ папку хранения файлов BMP
require[[Scripts\LUA\graph]]                       -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
LoadImage (path..[[Empire.bmp]])                   -- Загрузка экрана поиска (битовая маска №1)
LoadImage (path..[[goldF1.bmp]],2)                 -- Загрузка картинки поиска (битовая маска №2)
local L,w,h=GetBMI(1)                              -- Получение параметров изображения экрана (битовой маски №1)
local NotFirstFind = true                          -- установка флага признака что картинки пока не найдены
for i = 0, 200 do                                  -- цикл поиска граничных допустимых значений параметра "погрешность"
  --\/ поиск картинок с текущим значением параметра точность равным индексу цикла
  --\/ формирование искомой картинки (по битовой маске №2) выполняется непосредственно самой функцией поиска
  --\/ Фоновыми будут чёрные пиксели (r=0).
  local k, m = FindImage( 0, 30, w-1, h-1, { {r=0, dev=i} },   {nP=-1, shiX=20, shiY=3} )
  if NotFirstFind  and  k >= 22 then                 -- Если все картинки ещё не найдены  И  текущее количество найденных не менее 22 (всех искомых)
    if k == 22 then                                    -- Если найдено картинок столько, сколько нужно
        log("Максимально допустимая погрешность =",i)      -- Вывод максимально допустимого значения параметра точности
        NotFirstFind = false                               -- картинки найдены, сброс флага признака что картинки пока не найдены
      else                                               -- Иначе, картинок найдено больше имеющихся
        log("Ошибка - Картинки не найдены")                -- Вывод в лог об ошибке
        break                                              -- Прерывание цикла
    end
  end
  if k>22 then                                       -- Если количество найденных картинок больше искомых (ищется не то, что надо)
    log("Минимальный уровень погрешности =",i-1)       -- Вывод минимально допустимого значения параметра точности (предыдущее значение индекса цикла)
    break                                              -- Прерывание цикла
  end
end --------------------------------------------------------------------------------

ЛОГ информация результатов работы скрипта:

Максимально допустимая погрешность = 23
Минимальный уровень погрешности = 61

    Результат очень даже ничего, диапазон допустимой погрешности составляет 38 единиц. Это значит, что  подвержены
наиболее сильному рендерингу пиксели с низкими значениями канала BLUE, так как для жёлтых  пикселей  именно  канал
BLUE имеет наименьшее значение. Ещё раз взглянув на искомую картинку, видим, что  крайний  правый  столбец  жёлтых
пикселей действительно имеет низкие значения канала BLUE. Продолжим править картинку.  Закрасим  "чёрным"  крайний
правый столбец - имеем картинку goldF2.bmp. Кстати, это повысит и скорость поиска, так как будет меньше сравнений.
Правим скрипт определения диапазона точности, исправив в нём всего одну строку №6 (загрузка картинки).

--lua
local path = [[A:\find\]]                          -- !!!!! Укажите СВОЮ папку хранения файлов BMP
require[[Scripts\LUA\graph]]                       -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
LoadImage (path..[[Empire.bmp]])                   -- Загрузка экрана поиска (битовая маска №1)
LoadImage (path..[[goldF2.bmp]],2)                 -- Загрузка картинки поиска (битовая маска №2)
local L,w,h=GetBMI(1)                              -- Получение параметров изображения экрана (битовой маски №1)
local NotFirstFind = true                          -- установка флага признака что картинки пока не найдены
for i = 100, 0, -1 do                              -- цикл поиска граничных допустимых значений параметра "точность"
  --\/ поиск картинок с текущим значением параметра точность равным индексу цикла
  --\/ формирование искомой картинки (по битовой маске №2) выполняется непосредственно самой функцией поиска
  --\/ Фоновыми будут чёрные пиксели (r=0).
  local k, m = FindImage( 0, 30, w-1, h-1, { {r=0, acc=i} },   {nP=-1, shiX=20, shiY=3} )
  if NotFirstFind  and  k >= 22 then                 -- Если все картинки ещё не найдены  И  текущее количество найденных не менее 22 (всех искомых)
    if k == 22 then                                    -- Если найдено картинок столько, сколько нужно
        log("Максимально допустимая точность =",i)         -- Вывод максимально допустимого значения параметра точности
        NotFirstFind = false                               -- картинки найдены, сброс флага признака что картинки пока не найдены
      else                                               -- Иначе, картинок найдено больше имеющихся
        log("Ошибка - Картинки не найдены")                -- Вывод в лог об ошибке
        break                                              -- Прерывание цикла
    end
  end
  if k>22 then                                       -- Если количество найденных картинок больше искомых (ищется не то, что надо)
    log("Минимальный уровень точности =",i+1)          -- Вывод минимально допустимого значения параметра точности (предыдущее значение индекса цикла)
    break                                              -- Прерывание цикла
  end
end --------------------------------------------------------------------------------

ЛОГ информация результатов работы скрипта:

Максимально допустимая точность = 82
Минимальный уровень точности = 72

    Уже намного лучше. Диапазон 10% и главное прилично поднялась максимально допустимая точность - это хорошо.  Но
всё-таки очевидно, что именно канал BLUE является самым "узким местом поиска". Конечно лучше было бы  для  каждого
канала иметь свои индивидуальные параметры точности и погрешности, но я этого  не  реализовал,  вроде  и  так  всё
ищется. Конечно можно закрасить ещё один столбец, уверен - это значительно  и  улучшит,  и  ускорит  поиск,  но  я
намеренно не буду это делать. Попробуем просто "прикрутить" небольшое значение погрешности,  скажем  dev = 5,  тем
самым расширив немного диапазон поиска по каналу BLUE.
    Вообще, если нет уверенности в "стабильности" искомой картинки или искомая картинка вся "рябая", то оптимально
сочетать точность и погрешность в сочетании 2 к 1, например {acc=20, dev=10}.
    Итак, проверим, меняем условие формирование картинки добавив параметр dev=5, изменим 12 строку:
  local k, m = FindImage( 0, 30, w-1, h-1, { {r=0, acc=i} },   {nP=-1, shiX=20, shiY=3} )
на
  local k, m = FindImage( 0, 30, w-1, h-1, { {r=0, acc=i, dev=5} },   {nP=-1, shiX=20, shiY=3} )

ЛОГ информация результатов работы скрипта:

Максимально допустимая точность = 87
Минимальный уровень точности = 74

    Диапазон расширился до 13% и самое главное ещё на 5%  выше  поднялась  максимально  допустимая  точность.  Это
значит что картинки ищутся с большей достоверностью, поскольку максимум точности - это критерий, что картинки  уже
ищутся, а минимум точности - это критерий влияния иного содержимого экрана на результат поиска.
    Это уже вполне приемлемый результат. Конечно наверняка и этот результат можно  значительно  улучшить,  но цель
была показать логику поиска без многочисленных экспериментов. Я всё делал именно в  той  самой  последовательности
действий, какую и показал на примерах.
    Но! Всё-таки хочется показать как работают ДВА условия формирования искомой картинки, и как можно  значительно
улучшить и достоверность и скорость поиска, оставив предыдущие параметры без  изменений.  Мне  сразу  бросилось  в
глаза, что все монетки имеют белую окантовку. И хоть белые пиксели не являются "уникальными"  (присутствуют  и  на
изображениях молотов), они тем не менее могут послужить "ориентиром" поиска (а стОит ли искать  жёлтые  пиксели  в
текущей позиции поиска). Ещё раз внимательно посмотрим на картинки. На мой беглый взгляд  верхняя  граница  белого
канта более менее "стабильна". Ещё раз правим нашу картинку и вверху посередине дорисуем белый пиксель.  Поскольку
опять узким местом является канал BLUE (он часто имеет меньшее значение), рисуем пиксель с RGB=(255,255,240), чуть
уменьшив канал BLUE. На выходе картинка goldF3.bmp.
    

--lua
local path = [[A:\find\]]                          -- !!!!! Укажите СВОЮ папку хранения файлов BMP
require[[Scripts\LUA\graph]]                       -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
LoadImage (path..[[Empire.bmp]])                   -- Загрузка экрана поиска (битовая маска №1)
LoadImage (path..[[goldF3.bmp]],2)                 -- Загрузка картинки поиска (битовая маска №2)
local L,w,h=GetBMI(1)                              -- Получение параметров изображения экрана (битовой маски №1)
local NotFirstFind = true                          -- установка флага признака что картинки пока не найдены
for i = 100, 0, -1 do                              -- цикл поиска граничных допустимых значений параметра "точность"
  --\/ поиск картинок с текущим значением параметра точность равным индексу цикла
  --\/ формирование искомой картинки (по битовой маске №2) выполняется непосредственно самой функцией поиска
  --\/ !!!!! Добавляем ПЕРВОЕ условие, которое добавит ОДИН искомый белый пиксель в "трафарет" поиска.
  --\/       Для него жёстко ставим параметр 80% - устроят пиксели с минимальным RGB (204,204,192)
  --\/ Второе условие оставляем без изменений. Фоновыми будут чёрные пиксели (r=0) и по ним ведём статистику.
  local k, m = FindImage( 0, 30, w-1, h-1, { {b=240, fgr=true, acc=80}, {r=0, acc=i, dev=5} },   {nP=-1, shiX=20, shiY=3} )
  if NotFirstFind  and  k >= 22 then                 -- Если все картинки ещё не найдены  И  текущее количество найденных не менее 22 (всех искомых)
    if k == 22 then                                    -- Если найдено картинок столько, сколько нужно
        log("Максимально допустимая точность =",i)         -- Вывод максимально допустимого значения параметра точности
        NotFirstFind = false                               -- картинки найдены, сброс флага признака что картинки пока не найдены
      else                                               -- Иначе, картинок найдено больше имеющихся
        log("Ошибка - Картинки не найдены")                -- Вывод в лог об ошибке
        break                                              -- Прерывание цикла
    end
  end
  if k>22 then                                       -- Если количество найденных картинок больше искомых (ищется не то, что надо)
    log("Минимальный уровень точности =",i+1)          -- Вывод минимально допустимого значения параметра точности (предыдущее значение индекса цикла)
    break                                              -- Прерывание цикла
  end
end --------------------------------------------------------------------------------
     
ЛОГ информация результатов работы скрипта:

Максимально допустимая точность = 86
Минимальный уровень точности = 63

    Вот это уже достойнейший результат.  Диапазон варьирования  параметра  точность  во  втором  условии  (главном
идентификаторе картинки) составляет уже 23%. В конечном скрипте лучше всего использовать среднее значение, в нашем
случае соответственно acc=74. Такое значение даёт равноценный запас по достоверности поиска как по более  сильному
рендерингу самих картинок, так и запас на случай, если на экране будут  появляться  новые  объекты  которые  будут
"ухудшать" условия поиска. То, что верхняя граница снизилась на 1% - результат  ужесточения  поиска  по  ординате.
Надо понимать, что найденные координаты картинок не являются абсолютно точными и  запросто  могут  варьироваться в
пределах погрешности 1-2 пикселя, что вполне приемлемо.
    Вариант конечного скрипта:


--lua
local path = [[A:\find\]]                          -- !!!!! Укажите СВОЮ папку хранения файлов BMP
require[[Scripts\LUA\graph]]                       -- Загрузка представленного модуля с функциями поиска
log 'clear' log 'mode compact'
LoadImage (path..[[Empire.bmp]])                   -- Загрузка экрана поиска (битовая маска №1)
local L,w,h=GetBMI(1)                              -- Получение параметров изображения экрана (битовой маски №1)
local time=os.clock()
--\/ Поиск в битовой маске №1 (по умолчанию) картинки (goldF3.bmp), формируемой самой функцией поиска
local k, m = FindImage( 0, 30, w-1, h-1, { {nf=path..[[goldF3.bmp]], b=240, fgr=true, acc=80}, {r=0, acc=74, dev=5} },   {nP=-1, shiX=20, shiY=3} )
log ('Затрачено времени на поиск: ' .. tostring(os.clock() - time))
if k>0 then
    log("Найдено картинок - ",k)
    for i=1,math.min(k,30)do  -- вывод информации (не более чем для 30 картинок)
        log(i," - ",m[i][1],m[i][2]+30)
    end
  else  log("картинки не найдены")
end --------------------------------------------------------------------------------

==================================================================================================================

P.S.
    Извините за стиль кода модуля - мне так удобней, я "думаю глазами" и чем больше я вижу, тем мне комфортнее.
    Извините за не так "обозванные" переменные, как взбрело, так и обозвал.
    Извините, что нет комментов в самом модуле. Начал было делать, плюнул. Это я ещё пятилетку бы ковырялся.
    Ни на что не претендую - это просто вариант поиска. В принципе можно было конечно всё сделать по уму, через
    указатели, но делал для себя, а мне важнее скорость, пусть даже и небольшой прирост, да и как-то даже проще
    получилось.
    Перегружать примерами не стал, можете, если интересно, поискать сами кроме золота и ресурсы (картинка hammer).
    Обкатать толком функции просто не было времени. Всё пришлось переделывать под технологию Cirus-а, запросто
    мог где-нибудь что-нибудь недосмотреть. Свои скрипты только-только начал переводить на новые рельсы.
    Усовершенствовать на самом деле ещё много чего можно. Можно и автоматизировать процессы, чтобы картинки сами
    формировались так как надо, но до этого не дошли руки, да и надо ли ...
    Ломайте и меняйте всё на своё усмотрение. Творческих успехов! Будет критика - нормуль.
    Если найду ошибки - подправлю. Будут вопросы - отвечу, если будет время и если будет что отвечать.
