Помощь - Поиск - Пользователи - Календарь
Полная версия: Разработка findcolor, findimage
UoKit.com Форумы > Кликер > UO Pilot
Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
DarkMaster
Собственно обсуждение багов, фич, функционала, тесты и прочее.
sutra
Я в теме. Я давно отказался от передачи массивов. Делаю сразу глобальный массив (достаточно большой) через ffi , который использует и финдколор и все кому надо, и никаких тормозов. Если данные требуются позже (что крайне редко) ничто не мешает запомнить их отдельно. Понятно что это не эстетично, но совершенно нет потерь времени, необходимое при выделении памяти на массивы. По факту, при адекватных зонах поиска, время поиска - НУЛЬ.

Мне очень удобно. Грубо говоря вызываю if findcolor(... и данные поиска уже лежат в глобальном массиве и ими пользуешься напрямую в любых следующих действиях .

Сейчас буду пробовать твой вариант, если получится, то проблема будет решена для всех случаев жизни - спасибо!

Кстати, вот ещё замечание. От мощности видюхи зависит скорость доступа к данным, остальное от камня. Я успел до кризиса поменять на RTX 2070 - как чувствовал, что подорожают. Небольшой прирост в скорости всё-таки получил.
DarkMaster
Цитата
Я в теме.

Ловлю на слове. Пока хочу допилить, то, что есть, чтобы понять, как оно вообще работает, на чем сыпит скорость и т.д. У меня на данный момент финдколор на фул хд отрабатывает за 0.02 секунды, ирония в том что из этого 0.015 занимает парсинг параметров. Нужно допиливать. Код уже переписан раз на 10.

Цитата
Что-то у меня не получилось, выдаёт три нуля. Адрес присваивал шестнадцатиричное число из первого скрипта.

Можно в коде увидеть?
sutra
Не помню сколько у меня на фул хд. Я же сказал при "адекватных". Искать совсем не зная что и где - наверное тут и нет таких требований к скорости. Если искать мишени по всему экрану, то двух сотых вполне достаточно, чтобы потом всех их пристрелить, за полсекунды.

Всё ОК. Спасибо.
DarkMaster
Цитата
Не помню сколько у меня на фул хд. Я же сказал при "адекватных".

Ты где тут нашел адекватного?) Ну а вообще тесты то нужно как-то делать. На меньшем размере и вовсе на фоне парсинга хрен поймешь сколько времени занимает. На то они и тесты чтобы давать нагрузку.
sutra
Да я понял, но у меня в основном нагрузка на файндимидж. Поиск конечно идёт по локальным позициям, а не по всему экрану. Ищутся все кнопки, все цифры, все наименования. Один getimage и потом мгновенно всё распознаётся. Ищу как и говорил ранее, не картинки а силуэты!! Например силуэты цифр состоят из 2-х ... 5-ти пикселей, которые и анализируются. Сначала создаются готовые наборы таких силуэтов, от 3-х, до 6-ти на каждый символ. И этого хватает для однозначного поиска. Главное ручками (и головой) правильно создать эти силуэты. Я делаю для себя. А ты ставишь суперзадачу. Сделать алгоритм, работающий за тупого юзера. Будет время потестю, поделюсь результатами.

Конечно всё зависит от конкретной задачи, главное правильно определять где фон (его надо игнорировать) и где значимые пиксели (вот их и надо искать).
DarkMaster
По поводу создания изображений и помощи валенкам у меня тоже есть идеи, но это после поисковиков. По крайней мере нужно довести до ума. Обязательно хочу прилепить буфер из findString'а моего. Буфер изображений, чтобы они не дергались с винта это реально хорошая весч. Прикрутить возможность задать папку, а не конкретное изображение, дать возможность оверрайдить параметры поиска (если ищет всю папку) исходя из имени файла. Все это оптимизировать и вылизывать. Работы вагон. Но чет меня прет) Сижу с удовольствием неспешно тесты гоняю, переписываю все.
DarkMaster
Из забавного. Речь пойдет о чтении из памяти данных, которые уже существуют, их размер известен. Т.е. сознательно двигаются указатели, в т.ч. происходит выход за пределы массива (мы уверены, что за пределами массива все так же находятся данные). При тестах инициализация массивов, присвоение начальных адресов были вынесены за пределы бенчмарка. Т.е. непосредственно время обращения в if'ов.

local p = ffi.new("unsigned char[3]") - тормоз.
Гораздо эфективнее использовать конструкцию:
local p = ffi.new("unsigned char[1]")
двигая указатель на массив.

еще шустрее использовать:
ffi.cast("unsigned char*",address)
при этом если объявить:
local rmem = ffi.cast
rmem("unsigned char*",address)
то дополнительно выиграем в скорости.

rmem ("unsigned char*",i)[0],rmem ("unsigned char*",i+1)[0],rmem ("unsigned char*",i+2)[0]
проигрывает в скорости:
rmem ("unsigned char*",i)[0],rmem ("unsigned char*",i)[1],rmem ("unsigned char*",i)[2]
при этом вполне очевидно, что полей [1], [2] не существует, но все прекрасно работает - проверок на выходы за пределы массивов в ffi нет.
sutra
Ну вот, зачем спрашивается лопатить фул хд. Разбил на 4 зоны, запустил 4 скрипта и собирай урожай.
DarkMaster
ну вообще типа да, но типа нет. Поуму нужно делать потоки. Пока не берусь. Хотя бы потому, что не очень хорошо представляю сам для себя, как все это должно собираться. На данный момент есть четкое правило - все найденные результаты упорядочены слева-направо и сверху вниз, так же есть ограничение на количество найденных результатов. При разбиении на части может получиться путаница, если делать сортировку, то нужно дожидаться окончания обработки до некоторой позиции N в которой собрано необходимое количество изображений и т.д. Пока я для себя в голове не решил, как это будет выглядеть правильно и какое поведение будет наиболее востребовано и наиболее нативно ожидаемо для пользователя.
// Cirus подогнал getimage нормальный для работы через хендл.
sutra
rmem ("unsigned char*",i)[2]

Я собственно так и делаю в финдах, тоже в своё время тестировал.

Естественно потоки просто напросто правильнее.

А вот про getimage - очень интересно. Если поделитесь, буду рад. Во всяком случае у меня именно он является 100% якорем.
DarkMaster
Автор Cirus
code
Код
--lua
local ffi = require("ffi")
local SRCCOPY = 0x00CC0020
local DIB_RGB_COLORS = 0
local BI_RGB = 0
ffi.cdef[[
    typedef long LONG;
    typedef unsigned short WORD;
    typedef unsigned long DWORD;
    typedef unsigned char BYTE;
    typedef void *LPVOID;
     typedef struct {DWORD biSize; LONG  biWidth; LONG  biHeight; WORD  biPlanes; WORD  biBitCount; DWORD biCompression; DWORD biSizeImage;
        LONG  biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant;} BITMAPINFOHEADER;
    typedef struct {BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved;} RGBQUAD;
    typedef struct {BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1];} BITMAPINFO;
    int GetDC(int hWnd);
    int ReleaseDC(int hWnd, int hDC);
    int SelectObject(int hdc, int h);
    int CreateCompatibleDC(int hdc);
    int CreateCompatibleBitmap(int hdc, int cx, int cy);
    bool DeleteObject(int ho);
    bool BitBlt(int hdc, int x, int y, int cx, int cy, int hdcSrc, int x1, int y1, unsigned long rop);
    int GetDIBits(int hdc, int hbm, unsigned int start, unsigned int cLines, LPVOID lpvBits, BITMAPINFO* lpbmi, unsigned int usage);
]]

function Find(x, y, x2, y2, handle)
    local C = ffi.C
    local hdcWindow = C.GetDC(handle or 0)  -- если хендл не указан, то получим скрин с экрана
    local hdcMemDC = C.CreateCompatibleDC(hdcWindow)
    local hbmScreen = C.CreateCompatibleBitmap(hdcWindow, x2-x, y2-y)
    C.SelectObject(hdcMemDC,hbmScreen)
    C.BitBlt(hdcMemDC, 0, 0, x2-x, y2-y, hdcWindow, x, y, SRCCOPY)  -- сохранить в памяти скрин с окна или экрана

    local bi = ffi.new('BITMAPINFO', { {ffi.sizeof('BITMAPINFOHEADER'), x2-x, y2-y, 1, 32, BI_RGB,0,0,0,0,0} })
    C.GetDIBits(hdcWindow, hbmScreen, 0, y2-y, nil, bi, DIB_RGB_COLORS)   -- узнать какого размера нужен массив

    local bits = ffi.new('unsigned char[?]', bi.bmiHeader.biSizeImage)  -- выделить память под битовый массив
    local result = C.GetDIBits(hdcWindow, hbmScreen, 0, y2-y, bits, bi, DIB_RGB_COLORS)   -- получить битовый массив
--    log (result)   -- если функция выполнена успешно, то вернёт высоту изображения иначе 0
--    log('Размер массива: ' .. tostring(bi.bmiHeader.biSizeImage) .. ' байт')

    -- вывод в лог
    ---------------
--    local s = ''
--    local len = 0
--    local arr = {}
--    for i=0, bi.bmiHeader.biSizeImage-1, 4 do       -- тут поиск нужных пикселей
--        local R, G, B = bits[i+2], bits[i+1], bits[i]
--        s = s .. tostring(R + G * 256 + B * 65536) .. '  '
--        len = len + 1
--        if len == x2 - x then
--            table.insert(arr, 1, {s})
--            s = ''
--            len=0
--        end
--    end
--
--    for i = 1, #arr do
--        log (table.concat(arr[i], ' '))
--    end
    -------------------

    -- удаление ресурсов
    C.ReleaseDC(handle or 0, hdcWindow)
    C.DeleteObject(hdcMemDC)
    C.DeleteObject(hbmScreen)
end


log 'clear' log 'mode compact'
local t = os.clock()
Find(200, 100, 210, 115, workwindow()) -- координаты области 200, 100, 210, 115, последний параметр хендл окна(можно не указывать)
log('Время получения битового массива: ' .. tostring(os.clock()-t))
sutra
Понял лишь в общих чертах. Вопрос, а в файл сохранить картинку можно?
DarkMaster
Цитата
0, 0, x2-x, y2-y

Вот этот момент не совсем ясен. Почему 0 0? Почему сразу не вырезается нужный кусок?

Цитата
Понял лишь в общих чертах. Вопрос, а в файл сохранить картинку можно?

Любую битовую маску можно сохранить как бмп. У меня где-то был скрипт под это.
sutra
Я правильно понимаю, что мне надо добавить возврат значений из функции? В примере обработка ведётся прямо внутри функции.

Вот нет образования, вот и гадаю на кофейной гуще.

Адрес битовой маски фиксированный?? Или я не догоняю?

Короче, если довести до ума, то никаких параллельных скриптов не надо. А вообще конечно меня сильно удивляло, КАК механический винт маслает 60 МБ в сек. и как получить пару КБ с экрана - такие тормоза, понятно, что вопрос подхода к проблеме.

Насколько я всё-таки понял, всю аналитику надо вести внутри функции. Или не так??
DarkMaster
Пока кроме скорости ничего не тестил, по идее он вроде битовую маску как раз возвращает.
sutra
ОК. Буду ждать новостей. Cirus-у отдельное БОЛЬШОЕ спасибо.
cirus
Цитата
0, 0, x2-x, y2-y
Вот этот момент не совсем ясен. Почему 0 0?

Потому что 0 0 это координаты куда скопировать изображение, а не откуда. Откуда это x, y.
DarkMaster
Накидал кучу вопросов Cirus'у и надеюсь, что он меня не пошлет) В целом все готово. Осталось закрыть по getimage вопросы и отдам код на тесты и критику реализации. Вообще код когда-то выглядел очень красиво и стройно... Пока не прошли оптимизации... Но тут функция относительно высоконагруженная и зачастую требуется максимальная скорость реакции и снижение нагрузки на камень, так, что пусть лучше выглядит не очень, но зато работает, как надо.
DarkMaster
Собственно то, как вот оно есть.
findcolor manual
Код
-- findcolor
-- Функция поиска цвета либо нескольких цветов,
-- с возможностью задать некоторые отклонения в оттенке.
-- Синтаксис:
-- result = findcolor(<x_start, y_start, x_end, y_end | table_crds>,
-- <color> [,method [,count [,deviaton [,deviation_type [,abs_flag]]]])
--
-- <x_start, y_start, x_end, y_end | table_crds>
-- Координтаы задаются в виде прямоугольной области
-- с указанием координаты левого верхнего угла
-- и правого нижнего угла.
-- Координаты могут быть заданы четырмя переменными:
-- x_start, y_start, x_end, y_end
-- либо массивом с аналогичной структурой данных:
-- {x_start, y_start, x_end, y_end}
-- Отсчет координат начинатся с 0, а не 1.
-- Т.е. для FullHD область поиска будет
-- 0, 0, 1919, 1079.
--
-- <color>
-- Цвета, которые непосредственно ищются.
-- Синтаксис списка цветов:
-- <color | {color_1 [,color_2 [,... [,color_N]]]}>
-- Допустимые форматы цвета:
-- < dec_hex_color | dec_hex_color_1-dec_hex_color_2 |
-- {[r=val_1] [,g=val_2] [,b=val_3]} | [{r=val_1-val_2] [,g=val_3-val_4] [,b=val_5-val_6}] >
-- Форматы цветов можно кобинировать в рамках списка. Например:
-- 133972, 0x5060DD-0x5170DD, {r=10, g=0xFF, b=12-18}
--
-- [method]
-- Метод поиска. Значение по умолчанию: 2
-- 0/не задан     - Быстрый метод. Получить изображение всего экрана.
-- 1              - устаревший метод, используется для совместимости. Очень медленный.
--                  Для получения изображения всего экрана, а не окна используйте abs_flag.
-- 2              - надежный метод. Средняя скорость.
--                  Для получения изображения всего экрана, а не окна используйте abs_flag.
-- хендла_окна    - очень быстрый метод. Работает с перекрытыми окнами.
--                  Предпочтительно использовать именно его. Не работает с некоторыми приложениями.
--                  Для корректной работы может потребоваться задать хендл родительского окна.
-- адрес_картинки - Адрес изображения в формате bmp 24 бита.
--                  "my_image.bmp" - изображение рядом с exe пилота.
--                  "folder\\my_image.bmp" - изображение в папке folder рядом с exe пилота
--                  "\\my_image.bmp" - изображение в корне диска, на котором лежит пилот.
--                  [[d:\uopilot\images\my_image.bmp]] - абсолютный путь.
--                  Учтите, что при задании адресов в lua символ '\' необходимо удваивать,
--                  либо заменять на '/', либо брать весь адрес в двойные квадртные скобки.
-- адрес_памяти,  - Поиск в ранее полученном изображении по средством функции getimage()
-- высота_изобр,    Указывается адрес битовой маски, высота изображения, ширина и количество
-- ширина_изобр,    байт на каждую строку. Из-за выравнивания размер строки может быть
-- длина.           не кратным битности изображения. Данный параметр так же используется
--                  для определения формата битовой маски (24 бита либо 24 бита цвет + 8 резерв).
--
-- [count]
-- Количество искомых изображений. 0 | -1 - найти все.
--
-- [deviation]
-- Допустимые отклонения цвета.
-- Синтаксис:
-- deviation_general | {blue, green, red} |
-- {blue_bottom, green_bottom, red_bottom, blue_upper, green_upper, red_upper}
-- Отклонения цвета может быть задано одним числом на все каналы в + и в -,
-- либо на каждый канал отдельно,
-- либо на каждый канал отдельно и отдельно нижняя и верхняя граница канала.
--
-- [deviation_type]
-- Тип расчета допустимого отклонения цвета. Значение по умолчанию "r".
-- Возможные значения:
-- "a" - absolute. Абсолютное отклонение канала.
--       Например, при цвете 50 100 200 и абсолютном отклонении 10,
--       допустимый диапазон цветов будет равен 40-60, 90-110, 190-210
-- "r" - relative. Относительное отклонение, задается в процентах.
--       Например, при цвете 50 100 200 и относительном отклонении 10,
--       допустимый диапазон цветов будет равен 45-55 90-110 180-220.
--       Округление происход в сторону расширения диапазона.
--       Например, при значении канала 101 и допустимом отклонении 10%,
--       допустимыми значениями канала будут 101-11=90 101+11=112, т.е. 90-112.
-- "s" - shadow. Затемнение/осветление. Рассчитывается соотношение каналов, задается в процентах.
--       Данный метод может быть полезен, например, при смене суток в игре.
--       В рамках данного метода цвет 50 100 200 и цвет 25 50 100 - будут полностью идентичны.
--       Для указанных цветов: 200/50=4 50/100=0.5 100/200=0.5
--                             100/25=4  25/50=0.5  50/100=0.5
--       При допустимом отклонении в 10, будут считаться допустимыми соотношения каналов:
--       3.6-4.4 0.45-0.55 0.45-0.55
--
-- [abs_flag]
-- Флаг указывающий на то, что изображение должно быть получено не относительно с окна
-- к которому произведена привязка пилота через Ctrl+A или workwindow,
-- а относительно левого верхнего угла экрана.
-- Актуально для method 1, 2.

Мной сознательно проигнорированы shift_x shift_y из стандартного финдколора, которые вроде как были призваны снизить нагрузку. Они реально нужны? Я просто их не использовал никогда ввиду того, что смысла в них не видел, скорости относительно хватало, а вот пропустить что-нибудь важное можно. Если надо - вкручу.
Идеи/предложения/замечания?
cirus
Цитата
а в файл сохранить картинку можно?

Вариант1 (WriteFile)
Код
--lua
local ffi = require("ffi")
local SRCCOPY = 0x00CC0020
local DIB_RGB_COLORS = 0
local BI_RGB = 0
local GENERIC_WRITE = 0x40000000
local OPEN_ALWAYS = 4
local FILE_ATTRIBUTE_NORMAL = 0x00000080
ffi.cdef[[
    typedef long LONG;
    typedef unsigned short WORD;
    typedef unsigned long DWORD;
    typedef DWORD *LPDWORD;
    typedef unsigned char BYTE;
    typedef void *LPVOID;
    typedef const void *LPCVOID;
    #pragma pack (push, 1)
    typedef struct { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits;} BITMAPFILEHEADER;
    #pragma pack (pop)
     typedef struct {DWORD biSize; LONG  biWidth; LONG  biHeight; WORD  biPlanes; WORD  biBitCount; DWORD biCompression; DWORD biSizeImage;
        LONG  biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant;} BITMAPINFOHEADER;
    typedef struct {BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved;} RGBQUAD;
    typedef struct {BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1];} BITMAPINFO;
    int GetDC(int hWnd);
    int ReleaseDC(int hWnd, int hDC);
    int SelectObject(int hdc, int h);
    int CreateCompatibleDC(int hdc);
    int CreateCompatibleBitmap(int hdc, int cx, int cy);
    bool DeleteObject(int ho);
    bool BitBlt(int hdc, int x, int y, int cx, int cy, int hdcSrc, int x1, int y1, unsigned long rop);
    int GetDIBits(int hdc, int hbm, unsigned int start, unsigned int cLines, LPVOID lpvBits, BITMAPINFO* lpbmi, unsigned int usage);
    bool WriteFile(int hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, int lpOverlapped);
    int CreateFileA(const char* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
        int lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, int hTemplateFile);
    bool CloseHandle(int hObject);
]]

function screen(path, x, y, x2, y2, handle)
    local C = ffi.C
    local hdcWindow = C.GetDC(handle or 0)  -- если хендл не указан, то получим скрин с экрана
    local hdcMemDC = C.CreateCompatibleDC(hdcWindow)
    local hbmScreen = C.CreateCompatibleBitmap(hdcWindow, x2-x, y2-y)
    C.SelectObject(hdcMemDC,hbmScreen)
    C.BitBlt(hdcMemDC, 0, 0, x2-x, y2-y, hdcWindow, x, y, SRCCOPY)  -- сохранить в памяти скрин с окна или экрана

    local bi = ffi.new('BITMAPINFO', { {ffi.sizeof('BITMAPINFOHEADER'), x2-x, y2-y, 1, 32, BI_RGB,0,0,0,0,0} })
    C.GetDIBits(hdcWindow, hbmScreen, 0, y2-y, nil, bi, DIB_RGB_COLORS)   -- узнать какого размера нужен массив

    local bmpFileHeader = ffi.new('BITMAPFILEHEADER', {0x4d42, bi.bmiHeader.biSizeImage, 0, 0, ffi.sizeof('BITMAPFILEHEADER')+ffi.sizeof('BITMAPINFOHEADER')})
    local bits = ffi.new('unsigned char[?]', bi.bmiHeader.biSizeImage)
    local result = C.GetDIBits(hdcWindow, hbmScreen, 0, y2-y, bits, bi, DIB_RGB_COLORS)

    local dwWritten = ffi.new('DWORD[1]')
    local hFile = C.CreateFileA(path, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,0)

    C.WriteFile(hFile, bmpFileHeader, ffi.sizeof('BITMAPFILEHEADER'), dwWritten, 0)
    C.WriteFile(hFile, bi, ffi.sizeof('BITMAPINFOHEADER'), dwWritten, 0)
    C.WriteFile(hFile, bits, bi.bmiHeader.biSizeImage, dwWritten, 0)
    C.CloseHandle(hFile)

    C.ReleaseDC(handle or 0, hdcWindow)
    C.DeleteObject(hdcMemDC)
    C.DeleteObject(hbmScreen)
end


screen([[C:\screen_desktop.bmp]], 200, 200, 500, 500) -- скрин с экрана
screen([[C:\screen_window.bmp]], 200, 200, 500, 500, workwindow()) -- скрин с рабочего окна (может быть перекрыто)

Вариант2 (fwrite)
Код
--lua
local ffi = require("ffi")
local msvcrt = ffi.load('msvcrt.dll')
local SRCCOPY = 0x00CC0020
local DIB_RGB_COLORS = 0
local BI_RGB = 0
ffi.cdef[[
    typedef long LONG;
    typedef unsigned short WORD;
    typedef unsigned long DWORD;
    typedef DWORD *LPDWORD;
    typedef unsigned char BYTE;
    typedef void *LPVOID;
    typedef const void *LPCVOID;
    #pragma pack (push, 1)
    typedef struct { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits;} BITMAPFILEHEADER;
    #pragma pack (pop)
     typedef struct {DWORD biSize; LONG  biWidth; LONG  biHeight; WORD  biPlanes; WORD  biBitCount; DWORD biCompression; DWORD biSizeImage;
        LONG  biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant;} BITMAPINFOHEADER;
    typedef struct {BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved;} RGBQUAD;
    typedef struct {BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1];} BITMAPINFO;
    int GetDC(int hWnd);
    int ReleaseDC(int hWnd, int hDC);
    int SelectObject(int hdc, int h);
    int CreateCompatibleDC(int hdc);
    int CreateCompatibleBitmap(int hdc, int cx, int cy);
    bool DeleteObject(int ho);
    bool BitBlt(int hdc, int x, int y, int cx, int cy, int hdcSrc, int x1, int y1, unsigned long rop);
    int GetDIBits(int hdc, int hbm, unsigned int start, unsigned int cLines, LPVOID lpvBits, BITMAPINFO* lpbmi, unsigned int usage);
    int *fopen(const char *filename, const char *mode);
    int fwrite(const void *buffer, int size, int count, int *stream);
    int fclose(int *stream);
]]

function screen(path, x, y, x2, y2, handle)
    local C = ffi.C
    local hdcWindow = C.GetDC(handle or 0)  -- если хендл не указан, то получим скрин с экрана
    local hdcMemDC = C.CreateCompatibleDC(hdcWindow)
    local hbmScreen = C.CreateCompatibleBitmap(hdcWindow, x2-x, y2-y)
    C.SelectObject(hdcMemDC,hbmScreen)
    C.BitBlt(hdcMemDC, 0, 0, x2-x, y2-y, hdcWindow, x, y, SRCCOPY)  -- сохранить в памяти скрин с окна или экрана

    local bi = ffi.new('BITMAPINFO', { {ffi.sizeof('BITMAPINFOHEADER'), x2-x, y2-y, 1, 32, BI_RGB,0,0,0,0,0} })
    C.GetDIBits(hdcWindow, hbmScreen, 0, y2-y, nil, bi, DIB_RGB_COLORS)   -- узнать какого размера нужен массив

    local bmpFileHeader = ffi.new('BITMAPFILEHEADER', {0x4d42, bi.bmiHeader.biSizeImage, 0, 0, ffi.sizeof('BITMAPFILEHEADER')+ffi.sizeof('BITMAPINFOHEADER')})
    local bits = ffi.new('unsigned char[?]', bi.bmiHeader.biSizeImage)
    local result = C.GetDIBits(hdcWindow, hbmScreen, 0, y2-y, bits, bi, DIB_RGB_COLORS)

    local hFile = msvcrt.fopen(path, "wb")
    if hFile then
        msvcrt.fwrite(bmpFileHeader, ffi.sizeof('BITMAPFILEHEADER'), 1, hFile)
        msvcrt.fwrite(bi, ffi.sizeof('BITMAPINFOHEADER'), 1, hFile)
        msvcrt.fwrite(bits, bi.bmiHeader.biSizeImage, 1, hFile)
        msvcrt.fclose(hFile)
    else
        log('Файл не открылся')
    end

    C.ReleaseDC(handle or 0, hdcWindow)
    C.DeleteObject(hdcMemDC)
    C.DeleteObject(hbmScreen)
end


screen([[C:\screen_desktop.bmp]], 200, 200, 500, 500) -- скрин с экрана
screen([[C:\screen_window.bmp]], 200, 200, 500, 500, workwindow()) -- скрин с рабочего окна (может быть перекрыто)
sutra
Дарк, прочитал мануал. Что точно нужно добавить - это шаг поиска. Что возможно тоже может понадобиться - направление поиска. В остальном вроде всё как надо.

Cirus - спасибо большое. Пока ещё толком не тестил новые вещи. Чуть позже поделюсь мыслями.
sutra
Для чего нужен шаг? Например для поиска активной радиокнопки ("галочки", и т.п.)
DarkMaster
Как весело провести вечер и утро.

А знаете ли вы, что если получить указатель на локальный C массив и выйти за пределы видимости, то сборщик мусора в дебаге ее не почстит и ffi.cast будет работать, а если выключить дебаг, то переменная будет почищена и все отвалится? Включаешь дебаг - работает, выключаешь - не работает. Белочка рядом =)
sutra
Попробовал новый getimage. Работает намного быстрее. Прирост скорости зависит от зоны сканирования - это ОЧЕНЬ хорошо, так как нет принципиального тормоза.

Как я сейчас делаю. Получаю образ и потом на его базе ищу разные объекты (их немало). Потом чищу память.

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

Вот я и думаю. Может сделать массив bits глобальным. И пусть он потом просто обновляется. Выделить под него память исходя из возможного максимума и собственно всё. Если я где-то сильно не прав - поправьте.
DarkMaster
Цитата
Выделить под него память исходя из возможного максимума

А максимума нет. Размер окна может быть намного больше размера экрана. В свое время так скринил сайты (попросили).
Цитата
Иногда мне требуется работать с 2-мя образами, анализируя изменения (до и после). Как в таком случае?

Точно так же, как с обычным гетимиджем. Я просто делаю массив видимый только непосредственно гетимиджу и свой делит под это дело. Сегодня - завтара выложу, что получилось, дальше по ходу буду править.
sutra
Насколько я понимаю, по уму надо бы возвращать адрес массива bits, а по ненадобности освобождать память (как всё это делать я не знаю).
DarkMaster
Цитата
Насколько я понимаю, по уму надо бы возвращать адрес массива bits, а по ненадобности освобождать память (как всё это делать я не знаю).

Я так и делаю.

Все скоро будет =)
DarkMaster
Бэкапу пару дней. !@#$ notepad++
sutra
А тут как дела? Подвижки есть?
DarkMaster
Цитата
А тут как дела? Подвижки есть?

Есть, откатился из-за чудес выше до момента пока getimage от cirus не существовал. Восстанавливаю, переписываю заново. Кстати в коде cirus есть ошибки. В частности там берется разность координат, а должна браться ширина в части мест, так же есть вопросы по созданию массива через new, есть информация, что это в дальнейшем хорошие риски получить битый указатель из-за работы сборщика мусора. Вроде правильно надо через malloc.
DarkMaster
Захват изображения. Вот так вроде похоже на правду. Пока никуда еще в финды не подсовывал. Утрачено реально дофига, но там было очень много моментов, которые надо было понять и осознать. Восстановлю, конечно, шустрее, чем писать с нуля.

getimage

Код

local ffi=require "ffi"
local rmem=ffi.cast
local C=ffi.C

local SRCCOPY = 0x00CC0020
local DIB_RGB_COLORS = 0
local BI_RGB = 0
ffi.cdef[[
    typedef long LONG;
    typedef unsigned short WORD;
    typedef unsigned long DWORD;
    typedef unsigned char BYTE;
    typedef void *LPVOID;
    typedef struct {DWORD biSize; LONG  biWidth; LONG  biHeight; WORD  biPlanes; WORD  biBitCount; DWORD biCompression; DWORD biSizeImage;
        LONG  biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant;} BITMAPINFOHEADER;
    typedef struct {BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved;} RGBQUAD;
    typedef struct {BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1];} BITMAPINFO;
    int GetDC(int hWnd);
    int ReleaseDC(int hWnd, int hDC);
    int SelectObject(int hdc, int h);
    int CreateCompatibleDC(int hdc);
    int CreateCompatibleBitmap(int hdc, int cx, int cy);
    bool DeleteObject(int ho);
    bool BitBlt(int hdc, int x, int y, int cx, int cy, int hdcSrc, int x1, int y1, unsigned long rop);
    int GetDIBits(int hdc, int hbm, unsigned int start, unsigned int cLines, LPVOID lpvBits, BITMAPINFO* lpbmi, unsigned int usage);

    void free(void *ptr);
    void *malloc(size_t size);
]]

local ext = {}
local internal = {}
local images = {}

internal.deleteimage_orig = deleteimage
ext.getimage = function(x1, y1, x2, y2, handle, abs_flag)
    if handle > 2 or handle == 1 then
        local a, w, h, l
        w = x2-x1 + 1
        h = y2-y1 + 1
        
        local hdcWindow = C.GetDC(handle or 0)  -- если хендл не указан, то получим скрин с экрана
        local hdcMemDC = C.CreateCompatibleDC(hdcWindow)
        local hbmScreen = C.CreateCompatibleBitmap(hdcWindow, w, h)
        
        C.SelectObject(hdcMemDC,hbmScreen)
        C.BitBlt(hdcMemDC, 0, 0, w, h, hdcWindow, x1, y1, SRCCOPY)  -- сохранить в памяти скрин с окна или экрана

        local bi = ffi.new('BITMAPINFO', { {ffi.sizeof('BITMAPINFOHEADER'), w, -h, 1, 24, BI_RGB,0,0,0,0,0} })
        C.GetDIBits(hdcWindow, hbmScreen, 0, h, nil, bi, DIB_RGB_COLORS)   -- узнать какого размера нужен массив

        local bitmap_address = ffi.gc(C.malloc(bi.bmiHeader.biSizeImage), C.free)
        local h_copied = C.GetDIBits(hdcWindow, hbmScreen, 0, h, bitmap_address, bi, DIB_RGB_COLORS)   -- получить битовый массив


        --return h_copied and ffi.cast("int", bitmap_address) or nil, w, h_copied, math.floor(w*3/4+0.75)*4
        if h_copied > 0 then
            a = tonumber(ffi.cast("int", bitmap_address))
            
            images[a] = {}
            images[a].handle = handle
            images[a].hdcWindow = hdcWindow
            images[a].hdcMemDC = hdcMemDC
            images[a].hbmScreen = hbmScreen
            images[a].bitmap_address = bitmap_address

            return a, w, h_copied, math.floor(w*3/4+0.75)*4
        else
            return nil
        end
    else
        return internal.getimage_orig(x1, y1, x2, y2, handle, abs_flag)
    end
end

ext.deleteimage = function(address)
    if images[address] then
        C.ReleaseDC(images[address].handle, images[address].hdcWindow)            
        C.DeleteObject(images[address].hdcMemDC)
        C.DeleteObject(images[address].hbmScreen)
        ffi.C.free(ffi.gc(images[address].bitmap_address, nil)) -- Manually free the memory
        images[address] = nil
    else
        deleteimage_orig(address)
    end
end



Скорре всего:
Код
        C.ReleaseDC(images[address].handle, images[address].hdcWindow)             
        C.DeleteObject(images[address].hdcMemDC)
        C.DeleteObject(images[address].hbmScreen)

Можно выкинуть в getimage и прям там уничтожать, но я пока не тестил.

Ну и идейно я делаю замену пилотовских функций с полной обратной совместимостью. Т.е. предполагается:
deleteimage = ext.deleteimage
getimage = ext.getimage
без какого-либо ущерба для старых скриптов.
Cockney
Цитата(DarkMaster @ 29.3.2021, 17:09) *

Ну и идейно я делаю замену пилотовских функций с полной обратной совместимостью. Т.е. предполагается:
deleteimage = ext.deleteimage
getimage = ext.getimage
без какого-либо ущерба для старых скриптов.



Без ущерба это классно, но по своему опыту создания точно такого же костыля могу сказать, что правильней сделать кучу маленьких функций, засунуть их в условный core неймспейс, а уже потом из них лепить целевую функцию-замену.


Где-то на диске валяются потуги, на плюсы была портирована работа с мышью и клавиатурой. Если надо, могу опубликовать, да и кстати, а почему бы не сделать github проект для данного дела. Вроде 21 год, а версионирование через посты форума.
DarkMaster
Цитата
Где-то на диске валяются потуги, на плюсы была портирована работа с мышью и клавиатурой. Если надо, могу опубликовать, да и кстати, а почему бы не сделать github проект для данного дела. Вроде 21 год, а версионирование через посты форума.

Планов очень много, идей очень много. Я очень не хочу спешить. У меня появилось желание и возможность - я сидел ковырял некоторые баги пилота, некоторые потерянные возможности при переходе на луа. Если бы не sutra, то узнали бы об этом позже по релизу. Появится смысл - все будет. В целом я хочу сделать достаточно сильный упор на луа и возможность адаптации/прикручивания фишечек намного более нативным и спокойно расширяемым пользователем, форумными скриптами и т.д. Но давай не спешить. Сохранится возможность и желание - все будет. Вариант рассказать вагон баек и оставить все как было - меня не устраивает.
Цитата
Без ущерба это классно, но по своему опыту создания точно такого же костыля могу сказать, что правильней сделать кучу маленьких функций, засунуть их в условный core неймспейс, а уже потом из них лепить целевую функцию-замену.

Есть у меня два массивчика: internal и ext. Оба содержат только функции (не забываем про ограничение на 63 элемента в корне неймспейса в рамках луа). Соответственно ext - некоторый интерфейс поставляемый пользователю, internal - вся реальная обработка, куча фукнций для мелочей и т.д.
Cockney
Цитата(DarkMaster @ 29.3.2021, 21:00) *

Планов очень много, идей очень много. Я очень не хочу спешить. У меня появилось желание и возможность - я сидел ковырял некоторые баги пилота, некоторые потерянные возможности при переходе на луа. Если бы не sutra, то узнали бы об этом позже по релизу. Появится смысл - все будет. В целом я хочу сделать достаточно сильный упор на луа и возможность адаптации/прикручивания фишечек намного более нативным и спокойно расширяемым пользователем, форумными скриптами и т.д. Но давай не спешить. Сохранится возможность и желание - все будет. Вариант рассказать вагон баек и оставить все как было - меня не устраивает.



Не понимаю почему нельзя создать гитхаб реп уже сейчас и иметь нормальную кодовую базу, а не вот "ну опять этот блокнот...". Работа остановится - кто-то может продолжит. Это дело добровольное.
DarkMaster
Если угодно - сглазить не хочу.
Блокнот реально подвел тупо из-за того, что обновился. У меня по дефолту в бекапах чуть более, чем все =) Разные винты, разные верссии, что-то в оффлайн, что-то допом в облака. Произошло старое любимое "не было печали - апдейтов накачали".
sutra
Ну вот, я явился возмутителем спокойствия и благоденствия. Просто я ставил вполне себе понятные цели. Если мой глаз что-то видит и мозг даёт команду ткнуть куда-то, то это точно можно запрограммировать. Вопрос в степени сложности алгоритма (моё любимое занятие - придумывать). И именно из-за объёма вычислений и нужна эта самая скорость. Но даже реализация этих целей меня не устраивает. Я иду дальше, на основе собранной статистики - я пытаюсь ПРЕДСКАЗАТЬ то, что глаз ещё не видит. И это получается (цель ещё не появилась, а по факту она уже уничтожена - если очень грубо описать сей процесс).
Получается что это надо только мне. Но ради меня точно не надо напрягаться - для меня это просто хобби. Я своё давно отпрограммировал.
sutra
Попробовал сохранить картинку. Ничего не получилось. Пробовал всяко. С потерянной единичкой на ширине и высоте, без единички, 32 битами, 24 битами - результат один. Совершенно не догоняю почему зашит жёстко адрес 0x4d42 при инициализации заголовка файла.
cirus
Цитата
Совершенно не догоняю почему зашит жёстко адрес 0x4d42 при инициализации заголовка файла.

Это начало bmp файла BM.
Цитата
Попробовал сохранить картинку. Ничего не получилось.

Указать другую папку, возможно нет прав каких-то.
sutra
Создаёт файл 14 байт. Доступ к нему действительно отсутствует, пока не закроешь Пилот.

Пробовал в разные папки. Результат тот же.

Пишет только заголовок.

Всё от имени администратора. ???

Поменял местами, сначала пишу инфо выход - 40 байт. Значит блокирует после первой записи в файл. Что за хрень, непонятно.
sutra
Последовательно пишу заголовок. Перегружаю Пилот, инфо, перегружаю, маску. Потом всё в один файл - получил картинку 32 бита, 300х300. Всё дело в доступе. Ну и действительно единичка потеряна, должно быть 301х301.
sutra
Дарк, что-то я не понял как пользоваться твоим имиджем. Насколько я понимаю информация хранится в bitmap_address (по аналогии с bits). А как вытащить то?
DarkMaster
Значит так. Версия из активной разработки. Дальше функции нужно обязательно развернуть в тело финдколора - это существенно ускоряет работу. Но при этом вполне очевидно получается дикий свинарник, особенно если учесть необходимость дублирования циклов под каждый из типов поиска. Поэтому я прошу потестить именно в таком виде. В идеале было-бы что-нибудь типа теста, как я делал под color_deviation_тип_сравнения.
образец теста
Код

--log(ext.color_deviation_a(3302856, 56 *256*256 + 101 *256 + 200, 5) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_a(3302856, 44 *256*256 + 101 *256 + 200, 5) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_a(3302856, 45 *256*256 + 101 *256 + 200, 5) and "true  test: passed" or "false test: fail")
--log(ext.color_deviation_a(3302856, 55 *256*256 + 101 *256 + 200, 5) and "true  test: passed" or "false test: fail")
--log()
--log(ext.color_deviation_a(3302856, 50 *256*256 + 107 *256 + 200, 5) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_a(3302856, 50 *256*256 +  95 *256 + 200, 5) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_a(3302856, 50 *256*256 + 106 *256 + 200, 5) and "true  test: passed" or "false test: fail")
--log(ext.color_deviation_a(3302856, 50 *256*256 +  96 *256 + 200, 5) and "true  test: passed" or "false test: fail")
--log()
--log(ext.color_deviation_a(3302856, 50 *256*256 + 101 *256 + 206, 5) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_a(3302856, 50 *256*256 + 101 *256 + 194, 5) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_a(3302856, 50 *256*256 + 101 *256 + 205, 5) and "true  test: passed" or "false test: fail")
--log(ext.color_deviation_a(3302856, 50 *256*256 + 101 *256 + 195, 5) and "true  test: passed" or "false test: fail")
--log()
--log(ext.color_deviation_a(3302856, 50 *256*256 + 101 *256 + 200, 5) and "true  test: passed" or "false test: fail")
--log()
--log()
--log()
--log()
--
--log(ext.color_deviation_r(3302856, 56 *256*256 + 101 *256 + 200, 10) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_r(3302856, 44 *256*256 + 101 *256 + 200, 10) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_r(3302856, 45 *256*256 + 101 *256 + 200, 10) and "true  test: passed" or "false test: fail")
--log(ext.color_deviation_r(3302856, 55 *256*256 + 101 *256 + 200, 10) and "true  test: passed" or "false test: fail")
--log()
--log(ext.color_deviation_r(3302856, 50 *256*256 + 113 *256 + 200, 10) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_r(3302856, 50 *256*256 +  89 *256 + 200, 10) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_r(3302856, 50 *256*256 + 112 *256 + 200, 10) and "true  test: passed" or "false test: fail")
--log(ext.color_deviation_r(3302856, 50 *256*256 +  90 *256 + 200, 10) and "true  test: passed" or "false test: fail")
--log()
--log(ext.color_deviation_r(3302856, 50 *256*256 + 101 *256 + 221, 10) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_r(3302856, 50 *256*256 + 101 *256 + 179, 10) and "true  test: fail" or "false test: passed")
--log(ext.color_deviation_r(3302856, 50 *256*256 + 101 *256 + 180, 10) and "true  test: passed" or "false test: fail")
--log(ext.color_deviation_r(3302856, 50 *256*256 + 101 *256 + 220, 10) and "true  test: passed" or "false test: fail")
--log()
--log(ext.color_deviation_r(3302856, 50 *256*256 + 101 *256 + 200, 10) and "true  test: passed" or "false test: fail")
--log()
--log()
--log()
--log()

Ну т.е. оно тупо видно, если где-то сфейлило.

По части производительности однозначно стоит покопать internal.color_deviation_*, т.к. часть правок вроде бы ушла, но какая именно еще бы знать.

На данный момент код работает только с обязательным указанием deviation.
Метод поиска 1 вообще не тестил. Очень слабо представляю, как кто-то будет через get pix собирать картинку и ждать полчаса. Раньше были проблемы с ульимой и методом 2 - приходилось через get pix фигачить, но более этой проблемы в ультиме не наблюдаю, приложений которым нужен get pix я более не знаю.

По коду - все, что в ext массиве подразумевает импорт в пилот ну и соответственно работоспособность. Список с кратким описанием:
color_to_rgb - разделение на каналы
color_to_bgr - разделение на каналы
rgb_to_color - сбор каналов в код цвета
bgr_to_color - сбор каналов в код цвета
color_deviation_a - проверка точки на соотвествие цвета с абсолютным смещением оттенка
color_deviation_r - проверка точки на соотвествие цвета с отностиельным смещением оттенка
color_deviation_s - проверка точки на соотвествие цвета с затенением цвета
findcolor - поиск цвета

// небольшой оффтоп и почему получилось так, как оно получилось.
Собственно решил сделать что-то вроде пилотовского сравнение на дипазон цветовой. color_deviation_* это собственно то, что делалось (и отдаленно думалось про финдколр), когда появился sutra. Ну собственно отсюда и вся структура кода.
DarkMaster
собственно код
Код
--lua

local ffi=require "ffi"
local rmem=ffi.cast
local C=ffi.C

local SRCCOPY = 0x00CC0020
local DIB_RGB_COLORS = 0
local BI_RGB = 0
ffi.cdef[[
    typedef long LONG;
    typedef unsigned short WORD;
    typedef unsigned long DWORD;
    typedef unsigned char BYTE;
    typedef void *LPVOID;
    typedef struct {DWORD biSize; LONG  biWidth; LONG  biHeight; WORD  biPlanes; WORD  biBitCount; DWORD biCompression; DWORD biSizeImage;
        LONG  biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant;} BITMAPINFOHEADER;
    typedef struct {BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved;} RGBQUAD;
    typedef struct {BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1];} BITMAPINFO;
    int GetDC(int hWnd);
    int ReleaseDC(int hWnd, int hDC);
    int SelectObject(int hdc, int h);
    int CreateCompatibleDC(int hdc);
    int CreateCompatibleBitmap(int hdc, int cx, int cy);
    bool DeleteObject(int ho);
    bool BitBlt(int hdc, int x, int y, int cx, int cy, int hdcSrc, int x1, int y1, unsigned long rop);
    int GetDIBits(int hdc, int hbm, unsigned int start, unsigned int cLines, LPVOID lpvBits, BITMAPINFO* lpbmi, unsigned int usage);

    void free(void *ptr);
    void *malloc(size_t size);
]]

local ext = {}
local internal = {}
local images = {}

ext.wait           = {} -- блок дейстивий по ожиданию цвета/изображений/кликов/окон.
ext.wait.log       = 1  -- включить ввывод description в лог.
ext.wait.log_level = 2  -- уровень логирования для description.
ext.lg_level  = 1       -- уровень логирования. Для вывода сообщения должен быть больше или равен
                        -- заданному при вызове функции.
                        -- Если при вызове функции уровень не задан, то он считается равным 0.

function ext.lg(data, comment, level)
    if  (not level and ext.lg_level < 0) or (level and level < ext.lg_level) then
        return
    end
    if  comment ~= nil then
        log(comment)
    end

    local tab = ""
    local deep = 0

    local function show(data)
        -- Пишем в лог комментарий.
        deep = deep + 1 -- Уровень вложенности вызовов функции.

        if type(data) == "table"    then
            local element_counter = 0
            for k,v in pairs(data) do
                element_counter = element_counter + 1
                if  type (v) == "table" then
                    log(tab..'table: '..tostring(k))
                    tab = tab .. "    "
                    show(v)
                    tab = string.sub(tab, 1, -5)
                else
                    if     type(v) == "nil"       then v = "nil"
                    elseif type(v) == "string"    then v = '"' .. v .. '"'
                    elseif type(v) == "number"    then v = tostring(v)
                    elseif type(v) == "function"  then v = tostring(v)
                    elseif type(v) == "thread"    then v = tostring(v)
                    elseif type(v) == "userdata"  then v = "userdata"
                    elseif type(v) == "boolean"   then v = tostring(v)
                    elseif type(v) == "table"     then
                        log(tab..""..k.." = "..v or "nil")
                    else
                        v = "uknown data type"
                    end
                    log(tab..""..k.." = " .. v)
                end
            end
            log(tab.."".."Elements in table: " .. element_counter)
        else
            log('table is "' .. type(data) .. '" data type. Value: ' .. tostring(data))
        end
        
        --tab = ""
        --deep = 0
    end
    
    show(data)
end


ext.getimage = function(x1, y1, x2, y2, handle, abs_flag)
    if handle > 2 or handle == 0 then
        local a, w, h, l
        w = x2-x1 + 1
        h = y2-y1 + 1
        
        local hdcWindow = C.GetDC(handle or 0)  -- если хендл не указан, то получим скрин с экрана
        local hdcMemDC = C.CreateCompatibleDC(hdcWindow)
        local hbmScreen = C.CreateCompatibleBitmap(hdcWindow, w, h)
        
        C.SelectObject(hdcMemDC,hbmScreen)
        C.BitBlt(hdcMemDC, 0, 0, w, h, hdcWindow, x1, y1, SRCCOPY)  -- сохранить в памяти скрин с окна или экрана

        local bi = ffi.new('BITMAPINFO', { {ffi.sizeof('BITMAPINFOHEADER'), w, -h, 1, 24, BI_RGB,0,0,0,0,0} })
        C.GetDIBits(hdcWindow, hbmScreen, 0, h, nil, bi, DIB_RGB_COLORS)   -- узнать какого размера нужен массив

        local bitmap_address = ffi.gc(C.malloc(bi.bmiHeader.biSizeImage), C.free)
        local h_copied = C.GetDIBits(hdcWindow, hbmScreen, 0, h, bitmap_address, bi, DIB_RGB_COLORS)   -- получить битовый массив


        --return h_copied and ffi.cast("int", bitmap_address) or nil, w, h_copied, math.floor(w*3/4+0.75)*4
        if h_copied > 0 then
            a = tonumber(ffi.cast("int", bitmap_address))
            
            images[a] = {}
            images[a].handle = handle
            images[a].hdcWindow = hdcWindow
            images[a].hdcMemDC = hdcMemDC
            images[a].hbmScreen = hbmScreen
            images[a].bitmap_address = bitmap_address

            return a, w, h_copied, math.floor(w*3/4+0.75)*4
        else
            return nil
        end
    else
        return internal.getimage_orig(x1, y1, x2, y2, handle, abs_flag)
    end
end

internal.deleteimage_orig = deleteimage
ext.deleteimage = function(address)
    if images[address] then
        C.ReleaseDC(images[address].handle, images[address].hdcWindow)            
        C.DeleteObject(images[address].hdcMemDC)
        C.DeleteObject(images[address].hbmScreen)
        ffi.C.free(ffi.gc(images[address].bitmap_address, nil)) -- Manually free the memory
        images[address] = nil
    else
        deleteimage_orig(address)
    end
end

-- Раскладывает код цвета на отдельные каналы.
ext.color_to_rgb = function(c)
    local r,g,b
    b = math.floor(c/65536)
    g = math.floor(c/256-b*256)
    r = c-b*256*256-g*256
    return r, g ,b
end

ext.color_to_bgr = function(c)
    local r,g,b
    b = math.floor(c/65536)
    g = math.floor(c/256-b*256)
    r = c-b*256*256-g*256
    return b, g, r
end

ext.rgb_to_color = function(r, g, b)
    return b*256*256 + g*256 + r
end

ext.bgr_to_color = function(b, g, r)
    return b*256*256 + g*256 + r
end

--   ВНИМАНИЕ ФИКС СТАНДАРТНОЙ ФУНКИИ
--   ОРИГИНАЛЬНАЯ ФУНКЦИЯ ПОЛНОСТЬЮ ЗАМЕНЕНА
colortorgb = ext.color_to_rgb

internal.color_deviation_parse = function(b1, g1, r1, b2, g2, r2)
    if type(b1) == "table" then
    
    else
    
    end

    if not g1 then
        g1 = b1
        r1 = b1
        b2 = b1
        g2 = b1
        r2 = b1
    elseif not b2 then
        b2 = b1
        g2 = g1
        r2 = r1
    else
        -- Ставим минимальные значения вначале, максимальные вконце.
        b1, g1, r1, b2, g2, r2 = b2, g2, r2, b1, g1, r1
    end
    return b1, g1, r1, b2, g2, r2
end

--[[==
internal.color_deviation_parse = function(c1, c2, b1, g1, r1, b2, g2, r2)
    if type(b1) == "table" then
        if type(g1) == "table" then
            b2 = g1[1]
            g2 = g1[2]
            r2 = g1[3]
        else
            r2 = b2
            g2 = r1
            b2 = g1
        end
        r1 = b1[3]
        g1 = b1[2]
        b1 = b1[1]
    elseif type(b2) == "table" then
        r2 = b2[3]
        g2 = b2[2]
        b2 = b2[1]
    elseif not g1 and not r1 and not b2 and not g2 and not r2 then
        g1 = b1
        r1 = b1
        b2 = b1
        g2 = b1
        r2 = b1
    end

    if not b2 and not g2 and not r2 then
        r2 = r1
        g2 = g1
        b2 = b1
    end
    
    local c1r, c1g, c1b
    c1b = math.floor(c1/65536)
    c1g = math.floor(c1/256-c1b*256)
    c1r = c1-c1b*256*256-c1g*256
    
    local c2r, c2g, c2b
    c2b = math.floor(c2/65536)
    c2g = math.floor(c2/256-c2b*256)
    c2r = c2-c2b*256*256-c2g*256
    
    
    return b1, g1, r1, b2, g2, r2, c1r, c1g, c1b, c2r, c2g, c2b
end
]]

internal.color_deviation_a = function(c1b1, c1g1, c1r1, c1b2, c1g2, c1r2, c2b, c2g, c2r, b1, g1, r1, b2, g2, r2)
    if  c1r1 - b1 <= c2r and c1g1 - g1 <= c2g and c1b1 - r1 <= c2b and
        c1r2 + b2 >= c2r and c1g2 + g2 >= c2g and c1b2 + r2 >= c2b then
        return true
    else
        return false
    end
end

internal.color_deviation_r = function(c1b1, c1g1, c1r1, c1b2, c1g2, c1r2, c2b, c2g, c2r, b1, g1, r1, b2, g2, r2)
    local r1d = math.ceil(c1r1 * r1 * 0.01)
    local g1d = math.ceil(c1g1 * g1 * 0.01)
    local b1d = math.ceil(c1b1 * b1 * 0.01)
    
    local r2d = math.ceil(c1r2 * r2 * 0.01)
    local g2d = math.ceil(c1g2 * g2 * 0.01)
    local b2d = math.ceil(c1b2 * b2 * 0.01)

    if  c1r1 - r1d <= c2r and c1g1 - g1d <= c2g and c1b1 - b1d <= c2b and
        c1r2 + r2d >= c2r and c1g2 + g2d >= c2g and c1b2 + b2d >= c2b then
        return true
    else
        return false
    end
end

internal.color_deviation_s = function(c1b1, c1g1, c1r1, c1b2, c1g2, c1r2, c2b, c2g, c2r, br1, rg1, gb1, br2, rg2, gb2)
    c1b1, c1g1, c1r1, c1b2, c1g2, c1r2, c2b, c2g, c2r = c1b1+1, c1g1+1, c1r1+1, c1b2+1, c1g2+1, c1r2+1, c2b+1, c2g+1, c2r+1

    local c1br1 = c1b1/c1r1
    local c1rg1 = c1r1/c1g1
    local c1gb1 = c1g1/c1b1
    
    local c1br2 = c1b2/c1r2
    local c1rg2 = c1r2/c1g2
    local c1gb2 = c1g2/c1b2    
    
    local br_d1 = c1br1 * br1 * 0.01
    local rg_d1 = c1rg1 * rg1 * 0.01
    local gb_d1 = c1gb1 * gb1 * 0.01
    
    local br_d2 = c1br2 * br2 * 0.01
    local rg_d2 = c1rg2 * rg2 * 0.01
    local gb_d2 = c1gb2 * gb2 * 0.01
    
    local c2br = c2b/c2r
    local c2rg = c2r/c2g
    local c2gb = c2g/c2b
    
    --[[
    log(
    "", c1b1, c1g1, c1r1, "\r\n",
        c1b2, c1g2, c1r2, "\r\n",
        c2b, c2g, c2r
    )
    
    log(
    "", c1br1, c1rg1, c1gb1, "\r\n",
        c1br2, c1rg2, c1gb2, "\r\n",
        c2br,  c2rg,  c2gb
    )
    
    log(
    "", c1br1 , br_d1 , c2br , c1rg1 , rg_d1 , c2rg , c1gb1 , gb_d1 , c2gb , "\r\n",
        c1br2 , br_d2 , c2br , c1rg2 , rg_d2 , c2rg , c1gb2 , gb_d2 , c2gb
    )
    log(
    "", tostring(c1br1 - br_d1 <= c2br) , tostring(c1rg1 - rg_d1 <= c2rg) , tostring(c1gb1 - gb_d1 <= c2gb) , "\r\n",
        tostring(c1br2 + br_d2 >= c2br) , tostring(c1rg2 + rg_d2 >= c2rg) , tostring(c1gb2 + gb_d2 >= c2gb)
    )
    
    log(
    "", c1br1 - br_d1 ,"<=", c2br , c1rg1 - rg_d1 ,"<=", c2rg , c1gb1 - gb_d1 ,"<=", c2gb , "\r\n",
        c1br2 + br_d2 ,">=", c2br , c1rg2 + rg_d2 ,">=", c2rg , c1gb2 + gb_d2 ,">=", c2gb
    
    )
    ]]

    if  c1br1 - br_d1 <= c2br and c1rg1 - rg_d1 <= c2rg and c1gb1 - gb_d1 <= c2gb and
        c1br2 + br_d2 >= c2br and c1rg2 + rg_d2 >= c2rg and c1gb2 + gb_d2 >= c2gb then
        return true
    else
        return false
    end
end

internal.color_deviation = function(compare_func, c1, c2, b1, g1, r1, b2, g2, r2)
    local b1, g1, r1, b2, g2, r2 = internal.color_deviation_parse(b1, g1, r1, b2, g2, r2)
    local c1b1, c1g1, c1r1 = ext.color_to_bgr(c1)
    local c1b2 = c1b1
    local c1g2 = c1g1
    local c1r2 = c1r1
    
    local c2b, c2g, c2r = ext.color_to_bgr(c2)
    --log(c1b1, c1g1, c1r1, c1b2, c1g2, c1r2, c2b, c2g, c2r, b1, g1, r1, b2, g2, r2)
    return compare_func(c1b1, c1g1, c1r1, c1b2, c1g2, c1r2, c2b, c2g, c2r, b1, g1, r1, b2, g2, r2)
end

--[==[ color_deviation_a
-- Возвращает true, если результат
-- входит в допустимое отклонение цвета,
-- иначе вернет false.
-- Все цвета задаются в формате bgr, не rgb.
-- Пилот так же предоставляет цвета в формате bgr.
-- b, g, r в любом варианте могут быть заданы таблицей.
-- Допустимый синтаксис:
-- <цвет1>, <цвет2>, <value>
-- Значения допустимого отклонения
-- ситаются допустимыми +/- b, g, r.
-- <цвет1>, <цвет2>, <b, g, r>
-- Значения допустимого отклонения
-- ситаются допустимыми +/- b, g, r.
-- <цвет>, <цвет2>, <b1, g1, r1>, <b2[, g2[, r2]]>
-- Значения b1, g1, r1 считаются допустимыми в плюс.
-- Значения b2, g2, r2 считаются допустимыми в минус.
-- Минус не пишется.]==]

ext.color_deviation_a = function(c1, c2, b1, g1, r1, b2, g2, r2)  
    return internal.color_deviation(internal.color_deviation_a, c1, c2, b1, g1, r1, b2, g2, r2)
end

--[==[ color_deviation_r
-- Возвращает true, если результат
-- входит в допустимое отклонение цвета,
-- иначе вернет false.
-- Все цвета задаются в формате bgr, не rgb.
-- Пилот так же предоставляет цвета в формате bgr.
-- b, g, r в любом варианте могут быть заданы таблицей.
-- Погрешность задется в
-- ПРОЦЕНТАХ от текущего значения канала,
-- с округлением в бОльшую сторону.
-- Например при цвете точки: 50 101 200 и погрешности
-- равной 10% допустимая погрешность составит:
-- 50*10%=5, 101*10%=10.1=11, 200*10%=20
-- 50 101 200 исходный цвет
-- 45  90 180 минимально допустимый
-- 55 112 220 максимально допустимый
-- Допустимый синтаксис:
-- <цвет1>, <цвет2>, <value>
-- Значения допустимого отклонения
-- ситаются допустимыми +/- b, g, r.
-- <цвет1>, <цвет2>, <b, g, r>
-- Значения допустимого отклонения
-- ситаются допустимыми +/- b, g, r.
-- <цвет>, <цвет2>, <b1, g1, r1>, <b2[, g2[, r2]]>
-- Значения b1, g1, r1 считаются допустимыми в плюс.
-- Значения b2, g2, r2 считаются допустимыми в минус.
-- Минус не пишется.]==]

ext.color_deviation_r = function(c1, c2, b1, g1, r1, b2, g2, r2)  
    return internal.color_deviation(internal.color_deviation_r, c1, c2, b1, g1, r1, b2, g2, r2)
end

--[==[ color_deviation_s
-- Возвращает true, если результат
-- входит в допустимое отклонение цвета,
-- иначе вернет false.
-- Все цвета задаются в формате bgr, не rgb.
-- Пилот так же предоставляет цвета в формате bgr.
-- b, g, r в любом варианте могут быть заданы таблицей.
-- Погрешность задется в
-- ПРОЦЕНТАХ от ОТНОШЕНИЯ каналов друг к другу,
-- с округлением в бОльшую сторону.
-- Например при цвете точки: 50 101 200 и погрешности
-- равной 10% допустимая погрешность составит:
-- 50*10%=5, 101*10%=10.1=11, 200*10%=20
-- 50 101 200 исходный цвет
-- 45  90 180 минимально допустимый
-- 55 112 220 максимально допустимый
-- Допустимый синтаксис:
-- <цвет1>, <цвет2>, <value>
-- Значения допустимого отклонения
-- ситаются допустимыми +/- br, rg, gb.
-- <цвет1>, <цвет2>, <br, rg, gb>
-- Значения допустимого отклонения
-- ситаются допустимыми +/- br, rg, gb.
-- <цвет>, <цвет2>, <br1, rg1, gb1>, <br2[, rg2[, gb2]]>
-- Значения br1, rg1, gb1 считаются допустимыми в плюс.
-- Значения br2, rg2, gb2 считаются допустимыми в минус.
-- Минус не пишется.]==]

ext.color_deviation_s = function(c1, c2, br1, rg1, gb1, br2, rg2, bg2)  
    return internal.color_deviation(internal.color_deviation_s, c1, c2, br1, rg1, gb1, br2, rg2, bg2)
end

internal.parse_channel_to_min_max = function(v)
        local min, max
        if v then
            min, max = string.match (v, "([0-9x]+)-*([0-9x]*)")
            min = tonumber(min)
            max = tonumber(max)
            if not max then max = min end              
        else
            min, max = 0, 255
        end
        return min, max
    end

internal.parse_split_color_to_min_max_bgr = function(c)
    c = type(c) == "table" and c or {c}
    if c.r or c.g or c.b then
        c = {c}
    end
    local cc = ffi.new("uint8_t["..#c.."][6]")
    --log(type(c,#c))
    for i = 1, #c do
        if type(c[i]) == "table" then
            cc[i][0],cc[i][3] = internal.parse_channel_to_min_max(c[i].b)
            cc[i][1],cc[i][4] = internal.parse_channel_to_min_max(c[i].g)
            cc[i][2],cc[i][5] = internal.parse_channel_to_min_max(c[i].r)
        else
            local c1, c2 = string.match (c[i], "([0-9x]+)-*([0-9x]*)")
            c1 = tonumber(c1)
            c2 = tonumber(c2)
            cc[i][0] = math.floor(c1/65536)
            cc[i][1] = math.floor(c1/256-cc[i][0]*256)
            cc[i][2] = c1-cc[i][0]*256*256-cc[i][1]*256
            if c2 then
                cc[i][3] = math.floor(c2/65536)
                cc[i][4] = math.floor(c2/256-cc[i][3]*256)
                cc[i][5] = c2-cc[i][3]*256*256-cc[i][4]*256
            else
                cc[i][3],cc[i][4],cc[i][5] = cc[i][0],cc[i][1],cc[i][2]
            end
        end
    end

    return cc
end

internal.findcolor_deviation_parse = function(v)
    if type(v) == "number" then
        if v > 0 then
            v = {v, v, v, v, v, v}
        else
            v = nil -- вырубаем обработку отклонений.
        end
    elseif type(v) == "table" then
        vv[i][0],vv[i][3] = internal.parse_channel_to_min_max(c[i].b)
        vv[i][1],vv[i][4] = internal.parse_channel_to_min_max(c[i].g)
        vv[i][2],vv[i][5] = internal.parse_channel_to_min_max(c[i].r)
    end
    return v[1], v[2], v[3], v[4], v[5], v[6]
end

--[====[ findcolor
-- Функция поиска цвета либо нескольких цветов,
-- с возможностью задать некоторые отклонения в оттенке.
-- Синтаксис:
-- result = findcolor(<x_start, y_start, x_end, y_end | table_crds>,
-- <color> [,method [,count [,deviaton [,deviation_type [,abs_flag]]]])
--
-- <x_start, y_start, x_end, y_end | table_crds>
-- Координтаы задаются в виде прямоугольной области
-- с указанием координаты левого верхнего угла
-- и правого нижнего угла.
-- Координаты могут быть заданы четырмя переменными:
-- x_start, y_start, x_end, y_end
-- либо массивом с аналогичной структурой данных:
-- {x_start, y_start, x_end, y_end}
-- Отсчет координат начинатся с 0, а не 1.
-- Т.е. для FullHD область поиска будет
-- 0, 0, 1919, 1079.
--
-- <color>
-- Цвета, которые непосредственно ищются.
-- Синтаксис списка цветов:
-- <color | {color_1 [,color_2 [,... [,color_N]]]}>
-- Допустимые форматы цвета:
-- < dec_hex_color | dec_hex_color_1-dec_hex_color_2 |
-- {[r=val_1] [,g=val_2] [,b=val_3]} | [{r="val_1-val_2"] [,g="val_3-val_4"] [,b="val_5-val_6"}] >
-- Форматы цветов можно кобинировать в рамках списка. Например:
-- 133972, 0x5060DD-0x5170DD, {r=10, g=0xFF, b=12-18}
--
-- [method]
-- Метод поиска. Значение по умолчанию: 2
-- 0/не задан     - Быстрый метод. Получить изображение всего экрана.
-- 1              - устаревший метод, используется для совместимости. Очень медленный.
--                  Для получения изображения всего экрана, а не окна используйте abs_flag.
-- 2              - надежный метод. Средняя скорость.
--                  Для получения изображения всего экрана, а не окна используйте abs_flag.
-- хендла_окна    - очень быстрый метод. Работает с перекрытыми окнами.
--                  Предпочтительно использовать именно его. Не работает с некоторыми приложениями.
--                  Для корректной работы может потребоваться задать хендл родительского окна.
-- адрес_картинки - Адрес изображения в формате bmp 24 бита.
--                  "my_image.bmp" - изображение рядом с exe пилота.
--                  "folder\\my_image.bmp" - изображение в папке folder рядом с exe пилота
--                  "\\my_image.bmp" - изображение в корне диска, на котором лежит пилот.
--                  [[d:\uopilot\images\my_image.bmp]] - абсолютный путь.
--                  Учтите, что при задании адресов в lua символ '\' необходимо удваивать,
--                  либо заменять на '/', либо брать весь адрес в двойные квадртные скобки.
-- адрес_памяти,  - Поиск в ранее полученном изображении по средством функции getimage()
-- высота_изобр,    Указывается адрес битовой маски, высота изображения, ширина и количество
-- ширина_изобр,    байт на каждую строку. Из-за выравнивания размер строки может быть
-- длина.           не кратным битности изображения. Данный параметр так же используется
--                  для определения формата битовой маски (24 бита либо 24 бита цвет + 8 резерв).
--
-- [count]
-- Количество искомых изображений. 0 | -1 - найти все.
--
-- [deviation]
-- Допустимые отклонения цвета.
-- Синтаксис:
-- deviation_general | {blue, green, red} |
-- {blue_bottom, green_bottom, red_bottom, blue_upper, green_upper, red_upper}
-- Отклонения цвета может быть задано одним числом на все каналы в + и в -,
-- либо на каждый канал отдельно,
-- либо на каждый канал отдельно и отдельно нижняя и верхняя граница канала.
--
-- [deviation_type]
-- Тип расчета допустимого отклонения цвета. Значение по умолчанию "r".
-- Возможные значения:
-- "a" - absolute. Абсолютное отклонение канала.
--       Например, при цвете 50 100 200 и абсолютном отклонении 10,
--       допустимый диапазон цветов будет равен 40-60, 90-110, 190-210
-- "r" - relative. Относительное отклонение, задается в процентах.
--       Например, при цвете 50 100 200 и относительном отклонении 10,
--       допустимый диапазон цветов будет равен 45-55 90-110 180-220.
--       Округление происход в сторону расширения диапазона.
--       Например, при значении канала 101 и допустимом отклонении 10%,
--       допустимыми значениями канала будут 101-11=90 101+11=112, т.е. 90-112.
-- "s" - shadow. Затемнение/осветление. Рассчитывается соотношение каналов, задается в процентах.
--       Данный метод может быть полезен, например, при смене суток в игре.
--       В рамках данного метода цвет 50 100 200 и цвет 25 50 100 - будут полностью идентичны.
--       Для указанных цветов: 200/50=4 50/100=0.5 100/200=0.5
--                             100/25=4  25/50=0.5  50/100=0.5
--       При допустимом отклонении в 10, будут считаться допустимыми соотношения каналов:
--       3.6-4.4 0.45-0.55 0.45-0.55
--
-- [abs_flag]
-- Флаг указывающий на то, что изображение должно быть получено не относительно с окна
-- к которому произведена привязка пилота через Ctrl+A или workwindow,
-- а относительно левого верхнего угла экрана.
-- Актуально для method 1, 2.]====]

ext.findcolor = function(x1, y1, x2, y2, c, method, count, deviation, deviation_type, abs_crds)
local t = os.clock()
    if  type(x1) == "table" then
        abs_crds       = count
        deviation_type = method
        deviation      = c
        count          = y2
        method         = x2
        c              = y1    
    end
--log(1, os.clock() - t)    
    c                  = type(c) == "table" and c or {c}
    method             = method or workwindow()
    deviation          = internal.findcolor_deviation_parse(deviation)
    deviation_type     = deviation_type or "r"
--log(2, os.clock() - t)    
    local compare_func
    if deviation_type == "r" then
        compare_func = internal.color_deviation_r
    elseif deviation_type == "a" then
        compare_func = internal.color_deviation_a
    elseif deviation_type == "s" then
        compare_func = internal.color_deviation_s
    end
--log(3, os.clock() - t)    

    local offset_x1, offset_y1, offset_y1, offset_y2 = 0, 0, 0, 0
    local a, w, h, l
    if type(method) == "table" then
        a, w, h, l = method[1], method[2], method[3], method[4]
    elseif type(method) == "number" then
        a, w, h, l = getimage (x1, y1, x2, y2, method, abs_crds) -- getimage игнорирует x2, y2
        offset_x2 = w -    math.min(x2+1, w)  -- getimage игнорирует x2, y2
        offset_y2 = h - math.min(y2+1, h)  -- getimage игнорирует x2, y2
        saveimage(a,"image\\check.bmp")
    elseif type(method) == "string" then
        a, w, h, l = loadimage (method)
        offset_x1 =     math.min(x1,   w)
        offset_y1 =     math.min(y1,   h)
        offset_x2 = w -    math.min(x2+1, w)
        offset_y2 = h - math.min(y2+1, h)
    end
--log(4, os.clock() - t)    
    if not a then
        log("capture failed")
        stop_script()
    end
    log(a, w, h, l)
    
    local cc = internal.parse_split_color_to_min_max_bgr(c)

    local d = ffi.new("uint8_t[6]")
    d[0], d[1], d[2], d[3], d[4], d[5] = internal.findcolor_deviation_parse(deviation, d)


    local t = os.clock()
    
    log(a + offset_y1*l, a+(h-offset_y2)*l-1, l)
    
    local r = {}
    if deviation then
        for i = a + offset_y1*l, a+(h-offset_y2)*l-1, l do
            local y = i
            --p = zp + pa[0] + i / 3
            --log(222, tostring(p))
            --stop_script()
            for i = i+offset_x1*3, i+l-offset_x2*3-1, 3 do
                for j = 1, #c do    
                
                    --log(cc[j][0], cc[j][1], cc[j][2], cc[j][3], cc[j][4], cc[j][5], rmem("unsigned char*",i)[0],rmem("unsigned char*",i+1)[0],rmem("unsigned char*",i+2)[0], d[0], d[1], d[2], d[3], d[4], d[5])

                    --if  cc[j][0] - d[0] <= rmem("unsigned char*",i)[0] and cc[j][1] - d[1] <= rmem("unsigned char*",i)[1] and cc[j][2] - d[2] <= rmem("unsigned char*",i)[2] and
                     --   cc[j][3] + d[3] >= rmem("unsigned char*",i)[0] and cc[j][4] + d[4] >= rmem("unsigned char*",i)[1] and cc[j][5] + d[5] >= rmem("unsigned char*",i)[2] then
                    
                    --log((i-y)/3 + 1, (y-a)/l/3 + 1)
                    
                    if compare_func(cc[j][0], cc[j][1], cc[j][2], cc[j][3], cc[j][4], cc[j][5], rmem("unsigned char*",i)[0],rmem("unsigned char*",i)[1],rmem("unsigned char*",i)[2], d[0], d[1], d[2], d[3], d[4], d[5]) then                    
                        r[#r+1] = {}
                        r[#r][1] = (i-y)/3
                        r[#r][2] = math.floor((y-a)/l)
                        r[#r][3] = cc[j][1]*256*256 + cc[j][2]*256 + cc[j][3]
                        r[#r][4] = cc[j][4]*256*256 + cc[j][5]*256 + cc[j][6]
                        --log(r[#r][1],r[#r][2])
                        if count == #r then
                            return r
                        end
                    end
                end
                --p = p + 1
            end
        end
    else
        for y = 1, w do
            for x = 1, h do
                for i = 1, #deviation do
                    
                    if deviation_func() then
                    
                    end
                end
            end
        end
    end




--[[
    local pa = ffi.new("unsigned int[1]",a)
--local zp = ffi.new("unsigned char *")
    local p  = ffi.new("unsigned char[3]")

    local p = ffi.new("unsigned char *")
    p = p+pa[0]

--log(a)
--log(222, tostring(p))
--stop_script()
--log(333, #c)
    
--log(7, os.clock() - t)

    --ext.lg(d)
    --end_script()
    
--log(333, #c)    

--log(8, os.clock() - t)
local t = os.clock()
--log(l)
    local r = {}
    if deviation then
        for i = 0, h*l-1, l do
            local y = i
            --p = zp + pa[0] + i / 3
            --log(222, tostring(p))
            --stop_script()
            for i = i, i+w*3-1, 3 do
                for j = 1, #c do    
                --log(i)
                    --log(cc[j][0], cc[j][1], cc[j][2], cc[j][3], cc[j][4], cc[j][5], p[0], p[1], p[2], d[1], d[2], d[3], d[4], d[5], d[6])
                    --if compare_func(cc[j][0], cc[j][1], cc[j][2], cc[j][3], cc[j][4], cc[j][5], p[0], p[1], p[2], d[1], d[2], d[3], d[4], d[5], d[6]) then
                    --log(cc[j][0], cc[j][1], cc[j][2], cc[j][3], cc[j][4], cc[j][5], p[i], p[i+1], p[i+2], d[1], d[2], d[3], d[4], d[5], d[6])
                    if compare_func(cc[j][0], cc[j][1], cc[j][2], cc[j][3], cc[j][4], cc[j][5], p[i], p[i+1], p[i+2], d[1], d[2], d[3], d[4], d[5], d[6]) then                    
                        r[#r+1] = {}
                        r[#r][1] = (i-y)/3 + 1
                        r[#r][2] = (y)/l + 1
                        r[#r][3] = cc[j][1]*256*256 + cc[j][2]*256 + cc[j][3]
                        r[#r][4] = cc[j][4]*256*256 + cc[j][5]*256 + cc[j][6]  
                        --log(r[#r][1],r[#r][2])
                    end
                end
                --p = p + 1
            end
        end
    else
        for y = 1, w do
            for x = 1, h do
                for i = 1, #deviation do
                    
                    if deviation_func() then
                    
                    end
                end
            end
        end
    end
]]
    
deleteimage = internal.deleteimage
speed = speed + os.clock() - t
catch = #r
    
    return r
end


return ext


Прежде чем писать багрепорты на color_deviation_s и соотвественно метод 's' рекомендую сначала взять калькулятор и посчитать допустимые смещения) не все там так нативно.
sutra
Ни хрена себе ты наворочал. Тут месяц надо разбираться. Попробую. А надо то было просто добраться до битовой маски. Да пихнуть бы в файл без извращений.
DarkMaster
ext.lg - перекочевало из другого скрипта, в рамках color.lua поставляться не будет, вероятно будет жить в другой бибилотечке. Это из моих загашников.
sutra
Конечно моё мнение субъективно. Но вот зачем вообще нужен deviation? В имидже понятно, а в колоре зачем? Можно абсолютно точно задать требуемый диапазон поиска. Ради совместимости со старым хламом?
DarkMaster
Цитата
В имидже понятно, а в колоре зачем?

Идейно это был изначально возврат к пилотовскому:
if x y color1 color2
потом в рамках унификации ушло в общие алготритмы колора. Переписывалось раз 10. Там кстати парсинг цвета на середине откатившийся( Поправить нужно.
sutra
Может не доглядел (тогда ткни меня носом), но не нашёл поиска по RED-BLUE, RED-GREEN. Поиск по разности каналов куда как важнее deviation. Могу привести просто кучу примеров, когда это необходимо. Очень грубый пример, просто для понимания. Ну вот навёл прицел на мишень - прицел стал другим (обычно ярче) и, как правило, он легко ищется в обоих состояниях именно по разности между каналами.

Да и color не нужен. Тот же винд. А для совместимости проще сделать финд-олд.

У меня реализация именно такая, могу вызывать или как if findcolor (если нужно просто проверить) или b,k=findcolor (если нужно получить конкретные результаты)

Опять же сугубо лично моё субъективное мнение. Чем проще и элегантнее, тем лучше. Появились новые технологии - отказывайся от старых. Извозчиков оставим для туристов. А тащить за собой весь хлам совместимости - только ошибки и коллизии плодить.
DarkMaster
По разности каналов не делал. В достаточно большой мере метод 'r' подразумевает эту разность, равно, как и 's' в сочетании с некоторым диапазоном.
Цитата
Да и color не нужен.

Задач достаточно много. В частности я задолбался писать пачку ифов на диапазоны и переводить dec/hex, чтобы задать их. Ну и начал собственно этот момент фиксить. И тут остапа понесло)

Цитата
Появились новые технологии - отказывайся от старых.

попробуй использовать 'r'. Мне понарвилось =) Не знаю, как именно для твоих целей, но у меня очень хорошо отрабатывает. Ну и для меня это как раз во многом новая технология: вбил цвет, написал 5, 'r' и не паришься. Это проще, чем задавать каждый канал, а эффективность меня очень порадовала. На данный момент струтура такова, что добавить любой метод сравнения очень просто - для этого и есть набор функций сравнения. Загоням туда уже распарсенный диапазон, девиэйшн, делаем любую мамтематику, получаем результат. При этом так же не нужно обращать внимание на какие-либо размеры изображений, методы забора и т.д. Весь смысл в том, что в функцию сравнения прилетают уже коретные данные, обо всем остальном уже позаботились. Битовая маска перебирается только в рамках указанных координат(привет багам старого getimage и потеряным единичкам).
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.
Русская версия Invision Power Board © 2001-2024 Invision Power Services, Inc.