Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

UoKit.com Форумы _ UO Pilot _ Разработка findcolor, findimage

Автор: DarkMaster 24.3.2021, 18:01

Собственно обсуждение багов, фич, функционала, тесты и прочее.

Автор: sutra 25.3.2021, 16:34

Я в теме. Я давно отказался от передачи массивов. Делаю сразу глобальный массив (достаточно большой) через ffi , который использует и финдколор и все кому надо, и никаких тормозов. Если данные требуются позже (что крайне редко) ничто не мешает запомнить их отдельно. Понятно что это не эстетично, но совершенно нет потерь времени, необходимое при выделении памяти на массивы. По факту, при адекватных зонах поиска, время поиска - НУЛЬ.

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

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

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

Автор: DarkMaster 25.3.2021, 17:12

Цитата
Я в теме.

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

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

Можно в коде увидеть?

Автор: sutra 25.3.2021, 17:28

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

Всё ОК. Спасибо.

Автор: DarkMaster 25.3.2021, 17:28

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

Ты где тут нашел адекватного?) Ну а вообще тесты то нужно как-то делать. На меньшем размере и вовсе на фоне парсинга хрен поймешь сколько времени занимает. На то они и тесты чтобы давать нагрузку.

Автор: sutra 25.3.2021, 17:42

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

Конечно всё зависит от конкретной задачи, главное правильно определять где фон (его надо игнорировать) и где значимые пиксели (вот их и надо искать).

Автор: DarkMaster 25.3.2021, 17:50

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

Автор: DarkMaster 26.3.2021, 0:34

Из забавного. Речь пойдет о чтении из памяти данных, которые уже существуют, их размер известен. Т.е. сознательно двигаются указатели, в т.ч. происходит выход за пределы массива (мы уверены, что за пределами массива все так же находятся данные). При тестах инициализация массивов, присвоение начальных адресов были вынесены за пределы бенчмарка. Т.е. непосредственно время обращения в 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 26.3.2021, 16:43

Ну вот, зачем спрашивается лопатить фул хд. Разбил на 4 зоны, запустил 4 скрипта и собирай урожай.

Автор: DarkMaster 26.3.2021, 16:49

ну вообще типа да, но типа нет. Поуму нужно делать потоки. Пока не берусь. Хотя бы потому, что не очень хорошо представляю сам для себя, как все это должно собираться. На данный момент есть четкое правило - все найденные результаты упорядочены слева-направо и сверху вниз, так же есть ограничение на количество найденных результатов. При разбиении на части может получиться путаница, если делать сортировку, то нужно дожидаться окончания обработки до некоторой позиции N в которой собрано необходимое количество изображений и т.д. Пока я для себя в голове не решил, как это будет выглядеть правильно и какое поведение будет наиболее востребовано и наиболее нативно ожидаемо для пользователя.
// Cirus подогнал getimage нормальный для работы через хендл.

Автор: sutra 26.3.2021, 17:07

rmem ("unsigned char*",i)[2]

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

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

А вот про getimage - очень интересно. Если поделитесь, буду рад. Во всяком случае у меня именно он является 100% якорем.

Автор: DarkMaster 26.3.2021, 17:31

Автор 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 26.3.2021, 17:58

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

Автор: DarkMaster 26.3.2021, 18:00

Цитата
0, 0, x2-x, y2-y

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

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

Любую битовую маску можно сохранить как бмп. У меня где-то был скрипт под это.

Автор: sutra 26.3.2021, 18:11

Я правильно понимаю, что мне надо добавить возврат значений из функции? В примере обработка ведётся прямо внутри функции.

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

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

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

Насколько я всё-таки понял, всю аналитику надо вести внутри функции. Или не так??

Автор: DarkMaster 26.3.2021, 18:15

Пока кроме скорости ничего не тестил, по идее он вроде битовую маску как раз возвращает.

Автор: sutra 26.3.2021, 18:16

ОК. Буду ждать новостей. Cirus-у отдельное БОЛЬШОЕ спасибо.

Автор: cirus 27.3.2021, 1:59

Цитата
0, 0, x2-x, y2-y
Вот этот момент не совсем ясен. Почему 0 0?

Потому что 0 0 это координаты куда скопировать изображение, а не откуда. Откуда это x, y.

Автор: DarkMaster 27.3.2021, 11:41

Накидал кучу вопросов Cirus'у и надеюсь, что он меня не пошлет) В целом все готово. Осталось закрыть по getimage вопросы и отдам код на тесты и критику реализации. Вообще код когда-то выглядел очень красиво и стройно... Пока не прошли оптимизации... Но тут функция относительно высоконагруженная и зачастую требуется максимальная скорость реакции и снижение нагрузки на камень, так, что пусть лучше выглядит не очень, но зато работает, как надо.

Автор: DarkMaster 27.3.2021, 13:42

Собственно то, как вот оно есть.

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 27.3.2021, 21:21

Цитата
а в файл сохранить картинку можно?

Вариант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 28.3.2021, 10:15

Дарк, прочитал мануал. Что точно нужно добавить - это шаг поиска. Что возможно тоже может понадобиться - направление поиска. В остальном вроде всё как надо.

Cirus - спасибо большое. Пока ещё толком не тестил новые вещи. Чуть позже поделюсь мыслями.

Автор: sutra 28.3.2021, 10:41

Для чего нужен шаг? Например для поиска активной радиокнопки ("галочки", и т.п.)

Автор: DarkMaster 28.3.2021, 12:31

Как весело провести вечер и утро.

А знаете ли вы, что если получить указатель на локальный C массив и выйти за пределы видимости, то сборщик мусора в дебаге ее не почстит и ffi.cast будет работать, а если выключить дебаг, то переменная будет почищена и все отвалится? Включаешь дебаг - работает, выключаешь - не работает. Белочка рядом =)

Автор: sutra 28.3.2021, 13:43

Попробовал новый getimage. Работает намного быстрее. Прирост скорости зависит от зоны сканирования - это ОЧЕНЬ хорошо, так как нет принципиального тормоза.

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

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

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

Автор: DarkMaster 28.3.2021, 13:54

Цитата
Выделить под него память исходя из возможного максимума

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

Точно так же, как с обычным гетимиджем. Я просто делаю массив видимый только непосредственно гетимиджу и свой делит под это дело. Сегодня - завтара выложу, что получилось, дальше по ходу буду править.

Автор: sutra 28.3.2021, 13:59

Насколько я понимаю, по уму надо бы возвращать адрес массива bits, а по ненадобности освобождать память (как всё это делать я не знаю).

Автор: DarkMaster 28.3.2021, 14:03

Цитата
Насколько я понимаю, по уму надо бы возвращать адрес массива bits, а по ненадобности освобождать память (как всё это делать я не знаю).

Я так и делаю.

Все скоро будет =)

Автор: DarkMaster 28.3.2021, 23:12

Бэкапу пару дней. !@#$ notepad++


Эскизы прикрепленных изображений
Прикрепленное изображение

Автор: sutra 29.3.2021, 15:11

А тут как дела? Подвижки есть?

Автор: DarkMaster 29.3.2021, 15:51

Цитата
А тут как дела? Подвижки есть?

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

Автор: DarkMaster 29.3.2021, 17:09

Захват изображения. Вот так вроде похоже на правду. Пока никуда еще в финды не подсовывал. Утрачено реально дофига, но там было очень много моментов, которые надо было понять и осознать. Восстановлю, конечно, шустрее, чем писать с нуля.

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 29.3.2021, 20:33

Цитата(DarkMaster @ 29.3.2021, 17:09) *

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



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


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

Автор: DarkMaster 29.3.2021, 21:00

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

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

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

Автор: Cockney 29.3.2021, 23:47

Цитата(DarkMaster @ 29.3.2021, 21:00) *

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



Не понимаю почему нельзя создать гитхаб реп уже сейчас и иметь нормальную кодовую базу, а не вот "ну опять этот блокнот...". Работа остановится - кто-то может продолжит. Это дело добровольное.

Автор: DarkMaster 29.3.2021, 23:55

Если угодно - сглазить не хочу.
Блокнот реально подвел тупо из-за того, что обновился. У меня по дефолту в бекапах чуть более, чем все =) Разные винты, разные верссии, что-то в оффлайн, что-то допом в облака. Произошло старое любимое "не было печали - апдейтов накачали".

Автор: sutra 30.3.2021, 11:51

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

Автор: sutra 30.3.2021, 13:16

Попробовал сохранить картинку. Ничего не получилось. Пробовал всяко. С потерянной единичкой на ширине и высоте, без единички, 32 битами, 24 битами - результат один. Совершенно не догоняю почему зашит жёстко адрес 0x4d42 при инициализации заголовка файла.

Автор: cirus 30.3.2021, 13:43

Цитата
Совершенно не догоняю почему зашит жёстко адрес 0x4d42 при инициализации заголовка файла.

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

Указать другую папку, возможно нет прав каких-то.

Автор: sutra 30.3.2021, 15:31

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

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

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

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

Поменял местами, сначала пишу инфо выход - 40 байт. Значит блокирует после первой записи в файл. Что за хрень, непонятно.

Автор: sutra 30.3.2021, 15:44

Последовательно пишу заголовок. Перегружаю Пилот, инфо, перегружаю, маску. Потом всё в один файл - получил картинку 32 бита, 300х300. Всё дело в доступе. Ну и действительно единичка потеряна, должно быть 301х301.

Автор: sutra 30.3.2021, 17:38

Дарк, что-то я не понял как пользоваться твоим имиджем. Насколько я понимаю информация хранится в bitmap_address (по аналогии с bits). А как вытащить то?

Автор: DarkMaster 30.3.2021, 17:38

Значит так. Версия из активной разработки. Дальше функции нужно обязательно развернуть в тело финдколора - это существенно ускоряет работу. Но при этом вполне очевидно получается дикий свинарник, особенно если учесть необходимость дублирования циклов под каждый из типов поиска. Поэтому я прошу потестить именно в таком виде. В идеале было-бы что-нибудь типа теста, как я делал под 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 30.3.2021, 17:46

собственно код
Код
--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 30.3.2021, 17:47

Ни хрена себе ты наворочал. Тут месяц надо разбираться. Попробую. А надо то было просто добраться до битовой маски. Да пихнуть бы в файл без извращений.

Автор: DarkMaster 30.3.2021, 17:47

ext.lg - перекочевало из другого скрипта, в рамках color.lua поставляться не будет, вероятно будет жить в другой бибилотечке. Это из моих загашников.

Автор: sutra 30.3.2021, 18:16

Конечно моё мнение субъективно. Но вот зачем вообще нужен deviation? В имидже понятно, а в колоре зачем? Можно абсолютно точно задать требуемый диапазон поиска. Ради совместимости со старым хламом?

Автор: DarkMaster 30.3.2021, 18:19

Цитата
В имидже понятно, а в колоре зачем?

Идейно это был изначально возврат к пилотовскому:
if x y color1 color2
потом в рамках унификации ушло в общие алготритмы колора. Переписывалось раз 10. Там кстати парсинг цвета на середине откатившийся( Поправить нужно.

Автор: sutra 30.3.2021, 18:34

Может не доглядел (тогда ткни меня носом), но не нашёл поиска по RED-BLUE, RED-GREEN. Поиск по разности каналов куда как важнее deviation. Могу привести просто кучу примеров, когда это необходимо. Очень грубый пример, просто для понимания. Ну вот навёл прицел на мишень - прицел стал другим (обычно ярче) и, как правило, он легко ищется в обоих состояниях именно по разности между каналами.

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

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

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

Автор: DarkMaster 30.3.2021, 18:41

По разности каналов не делал. В достаточно большой мере метод 'r' подразумевает эту разность, равно, как и 's' в сочетании с некоторым диапазоном.

Цитата
Да и color не нужен.

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

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

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

Автор: sutra 30.3.2021, 18:43

Да я всё понимаю. Поэтому и не берусь претендовать на что-либо. Делаю всё чисто для себя. Просто ты просил мнение - НА получи. biggrin.gif

Автор: DarkMaster 30.3.2021, 18:43

Если есть любые идеи, как оно должно быть - готов изменить/поправить. Единственная просьба - максимально конкретно объяснить свое вИдение.

Автор: sutra 30.3.2021, 18:45

Да, кстати. Что-то у меня не получилось сохранить картинку твоим имиджем. Что не так делаю? Кинь конкретный примерчик.

Автор: DarkMaster 30.3.2021, 18:54

поправлен парсиг color_deviation_*
Код
--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 c2b, c2g, c2r = ext.color_to_bgr(c2)
    local cc = internal.parse_split_color_to_min_max_bgr(c1)

    return compare_func(cc[1][0], cc[1][1], cc[1][2], cc[1][3], cc[1][4], cc[1][5], 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


Цитата
Да, кстати. Что-то у меня не получилось сохранить картинку твоим имиджем. Что не так делаю? Кинь конкретный примерчик.

Дык я их и не сохранял =) Там где-то в отладке под старый getimage был saveimage. Скринилку пока не трогал.
Я тупо поставил в пеинте точки по углам и смотрел находит или нет (ну и в центре пару).

Если надо для отладки, то можно расскоментировать строку
--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])
Там покажет какие цвета передаются по каналам, какой цвет сравнивается, значения deviation.
Ну и можно еще расскоментить
--log(r[#r][1],r[#r][2])
будет при нахождении сразу показвать точки, но я для этого ext.lg обычно использовал. ext.lg - в лог пишет весь массив.

Автор: sutra 30.3.2021, 20:15

Вот объясните мне почему так? Просто хочу понять, чтобы быть в теме.

Дарк, у тебя отрицательное значение высоты:

local bi = ffi.new('BITMAPINFO', { {ffi.sizeof('BITMAPINFOHEADER'), w, -h, 1, 24, BI_RGB,0,0,0,0,0} })


А у первоисточника положительное:

local bi = ffi.new('BITMAPINFO', { {ffi.sizeof('BITMAPINFOHEADER'), x2-x, y2-y, 1, 32, BI_RGB,0,0,0,0,0} })

Автор: DarkMaster 30.3.2021, 20:18

Цитата
Дарк, у тебя отрицательное значение высоты:

Это является флагом писать строки сверху-вниз или снизу-вверх. Вопрос по поводу гениальности данного решения к майкрософту. Для меня это быдло-код =)
// Сам на красивый качественный код не претендую, но стараюсь стать лучше)

Автор: sutra 30.3.2021, 20:22

Спасибо, ну примерно так и подозревал. Плохо ничего не знать. sad.gif

Автор: DarkMaster 30.3.2021, 20:25

Цитата
Спасибо, ну примерно так и подозревал. Плохо ничего не знать.

Ну да, самое крутое не знать и писать. Думаешь я знал?) Cirus подсказал. А код он где-то нашел)) Зато как работает сарафанное радио "просвещение")) Один не знал, но подсказал, второй не знает и пишет, третий не знает и тестирует. Все прямо канонично.

Автор: sutra 30.3.2021, 20:30

Соответственно 32 битовая модель не требует контроля длины строки, правильно понимаю?

Ну в смысле кратности 4.

Автор: Madeus 30.3.2021, 20:32

speed = speed + os.clock() - t

Лишняя строчка?)

Автор: sutra 30.3.2021, 20:36

Эх! Понять бы ещё почему у меня проблемы с записью в файл bmp. Попробую-ка я комп перегрузить, наверное уже месяц маслает круглые сутки.

Автор: DarkMaster 30.3.2021, 20:42

Цитата
Лишняя строчка?)

бенчмарк. Ну для релиза лишняя, для работы не совсем.

Автор: sutra 30.3.2021, 20:44

Перегруз не помог. Нарушается у меня доступ к записываемому файлу. Очень странно, вроде, ну чего уж может быть проще.

Автор: Madeus 30.3.2021, 20:59

Цитата(DarkMaster @ 30.3.2021, 20:42) *

бенчмарк. Ну для релиза лишняя, для работы не совсем.

Я к тому что тестирую через require и вылетает с ошибкой на этой строчке)

Автор: DarkMaster 30.3.2021, 21:08

Можно удалить. Мой код тестов:

Код
speed = 0
catch = 0
local r

showwindow()
wait(100)

log(os.clock())
local t = os.clock()
for i = 1, 1 do
--    r = clr.findcolor(0, 0, 1919, 1079, {3302600,3302601,3302602,3302603,3302604}, "image/test.bmp", 100, 5, "a")
--    r = clr.findcolor(1810, 970, 1919, 1079, {3302600,3302601,3302602,3302603,3302604}, 593568, 100, 10, "r")
--    r = clr.findcolor(0, 0, 1919, 1079, 3302600, "image/test.bmp", 100, 6, "a")
--    r = clr.findcolor(0, 0, 1919, 1079, 3302600 , 2, 100, 6, "a")
end
log(os.clock() - t)
log(speed)
log(catch)

Автор: Madeus 30.3.2021, 21:19

Если не указать [deviation] вылетает с ошибкой .lua:489: attempt to index local 'v' (a nil value)
Если координаты указать массивом то вылетает с ошибкой .lua:614: bad argument #2 to 'min' (number expected, got nil)

Автор: DarkMaster 30.3.2021, 21:27

Цитата
Если не указать [deviation] вылетает с ошибкой

чуть выше писал, что на данный момент только со включенным девиэйшином работает. Причина тому - это реализовано по сути будет блочным копированием вместо вызова функций. Если сейчас сделать блочное копирование по 4 вариантам сравнения, то в итоге все правки нужно будет делать в 4 местах. a,r,s - должны работать.

Автор: Madeus 30.3.2021, 22:51

У меня одного если не указать "abs" то ищет в фулхд, хотя указаны конкретные координаты?

Лог без abs

Код

268554272 1921 1081 5764
268554272 273787983 5764


Лог с abs
Код

12398680 63 60 192
12398680 12410199 192



Кстати такой баг был замечен и со старым getimage

Автор: DarkMaster 31.3.2021, 0:00

Цитата
У меня одного если не указать "abs" то ищет в фулхд, хотя указаны конкретные координаты?

Какой метод при поиске указан?
При методе 2 будет использован старый getimage, он вернет изображение всего экрана, НО поиск должен быть именно в указанных координатах.

Автор: Madeus 31.3.2021, 0:12

Цитата(DarkMaster @ 31.3.2021, 0:00) *

Какой метод при поиске указан?
При методе 2 будет использован старый getimage, он вернет изображение всего экрана, НО поиск должен быть именно в указанных координатах.

Метод 2, тогда понятно, хотя надо проверить скорость.

Автор: DarkMaster 31.3.2021, 0:23

Это сделано исключительно для совместимости со старым кодом. Предполагается, что в дальнейшем основным методом будет забор по хендлу, либо если будут проблемы, то через метод 0, который по сути тоже самое, но не на приложение, а на весь экран. При методе 0 никто не мешает указать стартовые координаты приложения и его размер. Возможно имеет смысл сделать это более легким для использования(скрыто получать координаты приложения и его размер) и врубить там проверку abs.

Автор: DarkMaster 31.3.2021, 0:45

Sutra, я тут в итоге все-таки наткнулся на один ужас в загашниках... Сразу предупреждаю - это делалось, когда у нас был убогий crystal lua, не было luajit и все работало через Ж из-за этого crystal. Писалось чисто ради теста и экспериментов с графикой и пониманием bmp.
Короче скринилка есть, она работает, но нужно зажмуриться, скопировать код и никогда в него не смотреть. Она еще и дикий тормоз, но вроде шуршит.

зажмурься
Код

local ffi           = require[[ffi]]
local clr           = require[[lua_system\color]]

local function full_trash(path, a, w, h, l)

local binary = {}
function binary.writeInt(file, x)
    local b4=x%256  x=(x-x%256)/256
    local b3=x%256  x=(x-x%256)/256
    local b2=x%256  x=(x-x%256)/256
    local b1=x%256  x=(x-x%256)/256
    return file:write(string.char(b4,b3,b2,b1))

end

local f = io.open(path, 'wb')
f:write([[BM]])
binary.writeInt(f, h*w*3+54)
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0x36))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0x28))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0))
binary.writeInt(f, w)
binary.writeInt(f, h)
f:write(string.char(0x1))
f:write(string.char(0))
f:write(string.char(0x18))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0))
f:write(string.char(0))
binary.writeInt(f, w*h*3)
for i = 1, 16 do
    f:write(string.char(0))
end


--for i = a, a+l*w, l do
--for i = a, a+l*w, l do
log(a,l,h)
local i = a+l*h-l
log()
log(i,a)
while i >= a do
    for j = i, i+l-1, 3 do
        f:write(string.char(ffi.cast("unsigned char*", j)[0],
                            ffi.cast("unsigned char*", j)[1],
                            ffi.cast("unsigned char*", j)[2]   ))
    end
    i = i - l
end

f:close()
end

showwindow()
wait(100)

local a, w, h, l = clr.getimage(0,0,1919,1079,0)
full_trash([[image\my_pic.bmp]], a, w, h, l)



Автор: DarkMaster 31.3.2021, 2:06

Цитата
В имидже понятно, а в колоре зачем? Можно абсолютно точно задать требуемый диапазон поиска.

Кстати низя. Ты никак диапазоном не задашь, что r должен быть в 2 раза больше, чем g, с допустимым отклонением 10%.

Автор: Madeus 31.3.2021, 2:06

Цитата(Madeus @ 31.3.2021, 0:12) *

Метод 2, тогда понятно, хотя надо проверить скорость.

В общем findcolor в цикле прогоняет 20 зон
С abs справляется за 0.375
Без abs справляется за 0.696

Автор: DarkMaster 31.3.2021, 2:43

Цитата
С abs справляется за 0.375
Без abs справляется за 0.696

В обоих случаях зоны имели одинаковые цвета?
Метод 2?
Входные данные какие?

Автор: cirus 31.3.2021, 3:01

Цитата
В общем findcolor в цикле прогоняет 20 зон

Сделать скрин и искать на скрине, быстрее будет.

Автор: DarkMaster 31.3.2021, 3:06

100 прогонов во всех тестах

Фулл хд. Абс выкл.
r = clr.findcolor(0, 0, 1919, 1079, 3302600 , 2, 100, 6, "a")
полное время выполнения: 8.4 секунды.
время анализа изображения: 0.36 секунды.

Фулл хд. Абс вкл.
r = clr.findcolor(0, 0, 1919, 1079, 3302600 , 2, 100, 6, "a", "abs")
полное время выполнения: 9.941
время анализа изображения: 0.37000000000018

Область 1/100 фулл хд. Абс выкл.
r = clr.findcolor(0, 0, 191, 107, 3302600 , 2, 100, 6, "a")
end
полное время выполнения: 8.789
время анализа изображения: 0.0039999999999054

Область 1/100 фулл хд. Абс вкл.
r = clr.findcolor(0, 0, 191, 107, 3302600 , 2, 100, 6, "a", abs)
полное время выполнения: 8.4830000000001
время анализа изображения: speed: 0.010999999999967

speed это как раз для оценки скорости алгоритма сравнения.
Все остальное уходит на парсинг и захват изображения.

В целом абс существенным образом не повлияло на общее время выполнения. Тем не менее по каким-то причинам захват большой области происходил чуть дольше именно с абс, чем такой же области без абс.
При уменьшении области в 100 раз можно наблюдать идеально ровное уменьшение времени поиска в 100 раз в рамках именно анализа изображения. Почему при абс время поиска сокращается только в 30 раз для меня некоторая загадка.
Если взглянуть на картину в целом, то время анализа просто тонет во времени захвата изображения. В среднем анализ занял 4% времени при поиске фул хд, и 0.087% при поиске в области 1/100 от фулл хд.

Автор: sutra 31.3.2021, 11:27

Цитата
Короче скринилка есть, она работает, но нужно зажмуриться, скопировать код и никогда в него не смотреть

Спасибо Дарк. Конечно так я и сам могу, но по примеру будет проще.

То, что в lua убогая работа с файлами - это всем известный факт. Меня волнует другое. Я терпеть ненавижу непонятки. Любая непонятка - источник ошибок в будущем. Вопрос то, почему Пилот блокирует файл, почему блокирует только у меня - это ещё интереснее. Или кроме меня больше никто не пробовал код cirus-а. Почему на казалось бы самом продвинутом низком уровне такое происходит. А может есть уровень повыше? Ну например как в дельфине rewrite, blockwrite.

Автор: DarkMaster 31.3.2021, 13:03

Я его код не тестил. Мне скрины на тот момент были не нужны совсем, а одновременно заниматься двумя разными вещами я не могу. Сегодня гляну.

Автор: Madeus 31.3.2021, 17:20

Цитата(DarkMaster @ 31.3.2021, 3:06) *

В целом абс существенным образом не повлияло на общее время выполнения. Тем не менее по каким-то причинам захват большой области происходил чуть дольше именно с абс, чем такой же области без абс.
При уменьшении области в 100 раз можно наблюдать идеально ровное уменьшение времени поиска в 100 раз в рамках именно анализа изображения. Почему при абс время поиска сокращается только в 30 раз для меня некоторая загадка.
Если взглянуть на картину в целом, то время анализа просто тонет во времени захвата изображения. В среднем анализ занял 4% времени при поиске фул хд, и 0.087% при поиске в области 1/100 от фулл хд.

Я просмотрел код и возник вопрос. В коде получается используем старый getimage? поправил, потестил ощутимо быстрее с новым getimage. В 100 раз с методом хендла окна. С abs 0 и 2 (старый getimage) метод разницы нет, а без abs разница более чем в 3 раза.

Автор: DarkMaster 31.3.2021, 17:44

Накидал скринилку. Фиксировные битовые константы заголовков сделаны сознательно с целью быть полностью уверенным в том формате, который будет использован. Сюрприз, но bmp продолжает развиваться и у меня не очень много желания обнаружить потом какие-нибудь непереносимые на другие системы скрины или что-нибудь в этом роде. Функция временно требует задания w, h, l. В дальнейшем будет все дергаться из общего массива/буфра загруженных изображений.

скринилка
Код
ext.saveimage = function(path, a, w, h, l)end
do
    local FILE_READ_DATA = 0x1
    local FILE_WRITE_DATA = 0x2

    local FILE_SHARE_READ = 0x00000001
    local FILE_SHARE_WRITE = 0x00000002

    local CREATE_ALWAYS = 0x2

    local FILE_ATTRIBUTE_NORMAL = 0x80

    local bmp_headers = {}
        
    bmp_headers[1] = ffi.new("const unsigned char[2]",{ 0x42,
                                                        0x4D}    )
                                                    --  file_size 4 bytes
    bmp_headers[2] = ffi.new("const unsigned char[12]",{0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x36,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x28,
                                                        0x00,
                                                        0x00,
                                                        0x00    })
                                                    --    image_width 4 bytes signed integer
                                                    --  image_height 4 bytes signed integer
    bmp_headers[3] = ffi.new("const unsigned char[8]",{ 0x01,
                                                        0x00,
                                                        0x18, -- the number of bits per pixel, which is the color depth of the image. Typical values are 1, 4, 8, 16, 24 and 32
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00    })
                                                    --  bitmap_size (w*h*3)
    bmp_headers[4] = ffi.new("const unsigned char[16]",{0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00,
                                                        0x00    })

    ext.saveimage = function(path, a, w, h, l)
        local f = C.CreateFileA(
            path,
            FILE_READ_DATA + FILE_WRITE_DATA,
            FILE_SHARE_READ + FILE_SHARE_WRITE,
            nil,
            CREATE_ALWAYS,
            FILE_ATTRIBUTE_NORMAL,
            nil)

        local dwbuf = ffi.new'DWORD[1]'


        local success = C.WriteFile(f, bmp_headers[1],                       2, dwbuf, nil)
        local success = C.WriteFile(f, ffi.new("uint32_t[1]", h*w*3+54),     4, dwbuf, nil)
        local success = C.WriteFile(f, bmp_headers[2],                      12, dwbuf, nil)
        local success = C.WriteFile(f, ffi.new("uint32_t[1]", w),            4, dwbuf, nil)
        local success = C.WriteFile(f, ffi.new("uint32_t[1]", -h),           4, dwbuf, nil)
        local success = C.WriteFile(f, bmp_headers[3],                       8, dwbuf, nil)
        local success = C.WriteFile(f, ffi.new("uint32_t[1]", w*h*3),        4, dwbuf, nil)
        local success = C.WriteFile(f, bmp_headers[4],                      16, dwbuf, nil)
        local success = C.WriteFile(f, ffi.cast("const void*", a),                                  h*l, dwbuf, nil)

        C.CloseHandle(f)
    end
end


Цитата
В коде получается используем старый getimage?

Только в случае, если используется метод 2 или метод 1. Код по захвату этими методами не переписывался. В первую очередь это обусловлено тем, что смысла нет. Дергать нужно или по хендлу или через 0 - это шустро, проблем, как правило нет. 0 имеет все шансы заменить метод 2.

Автор: Madeus 31.3.2021, 17:51

Цитата(DarkMaster @ 31.3.2021, 17:44) *

Только в случае, если используется метод 2 или метод 1. Код по захвату этими методами не переписывался. В первую очередь это обусловлено тем, что смысла нет. Дергать нужно или по хендлу или через 0 - это шустро, проблем, как правило нет. 0 имеет все шансы заменить метод 2.

Тогда вопрос
Код

a, w, h, l = getimage (x1, y1, x2, y2, method, abs_crds) -- getimage игнорирует x2, y2

Тут мы вызываем getimage, подразумевается что это новый getimage, но в коде нигде нет ссылки на него.
Так же нет строки
Код
internal.getimage_orig = getimage

которая вызовет старый getimage в случае метода 1 и 2, 1 кстати не работает, но он и не нужен)

Собственно я добавил строчку выше и вызываю через ext.getimage
Идейно думаю было что-то типо
Код

internal.getimage_orig = getimage
ext.getimage = function(x1, y1, x2, y2, handle, abs_flag)
    код
end
getimage = ext.getimage
a, w, h, l = getimage (x1, y1, x2, y2, method, abs_crds) -- getimage игнорирует x2, y2

Или я что-то не понял?

Автор: sutra 31.3.2021, 17:59

Тест старого и нового имиджа по хендлу

Код

зона        old           new
800x800   28.798       5.506
400x400   28.439       1.809
200x200   28.642       1.045
100x100   28.422       0.623
50x50    28.533       0.686

Выполнялся цикл на 1000 итераций.
Как уже говорил ранее, в старом однозначно где-то присутствует якорь.
В новом скорость зависит от зоны захвата.

Автор: DarkMaster 31.3.2021, 18:07

Круто я когда-то лажанул. Убрал объявление getimage = ext.getimage, а в коде не заменил. И где должен был использоваться новый захват - получился старый гетимидж.

Цитата
Идейно думаю было что-то типо

Дык оно и было. Потом было подчищено чтобы разделить вызовы на время тестов.

Автор: sutra 31.3.2021, 18:12

Хотя тестить в lua - гиблое дело. Можно лишь приблизительно судить о быстродействии. Вроде делаешь каждый раз одно и то же, а разброс по времени бывает ну очень даже приличный.

Я не стал ничего менять, пусть будет старый, пусть будет новый. Кто его знает этот ваш новый - посмотрим ещё будет ли вести себя примерно.

При желании одним росчерком пера можно сразу поменять во всех файлах одновременно старое на новое. Не вижу тут никаких проблем.

Автор: Madeus 31.3.2021, 18:14

Цитата(DarkMaster @ 31.3.2021, 18:07) *

Круто я когда-то лажанул. Убрал объявление getimage = ext.getimage, а в коде не заменил. И где должен был использоваться новый захват - получился старый гетимидж.

Бывает smile.gif Я сам такой же, че то решил потестить: там убрал тут добавил где-то забыл все сломалось...

Автор: DarkMaster 31.3.2021, 18:44

getimage 100 итераций
вызов + удаление изображения FullHD
старый 4.80
новый 1.65

вызов + удаление изображения 1/100 FullHD
старый 0.93
новый 0.08

Абс (метод 0) только с новым ибо я не уверен насколько корректно можно вызвать захват по хендлу с абс в рамках старого синтаксиса (не документировано)
FullHD 6.0240000000013
1/100 FullHD 2.5

Цитата
Хотя тестить в lua - гиблое дело.

Я тоже так думал, но при достаточном количестве итераций и контролем входных данных (изображение тоже входные данные), можно получить релевантные результаты.

Sutra, скринилку нормальную тестил? Вторая которая.

Прикрепленный файл  color.lua ( 31,04 килобайт ) Кол-во скачиваний: 90

Автор: sutra 31.3.2021, 19:05

Дарк, спасибо за скринилку. Всё, на сегодня хватит. Всё буду смотреть завтра. Наплевать на развитие, заголовок подправить плёвое дело. Я ж говорил, у меня Пилот блокировал файл. На крайняк всегда можно посимвольно в lua записывать. Хотя честно, я просто не представляю как до такого кто-то додумался. 30 лет назад и то всё круче. Тот же Паскаль из того же буфера, хоть на низком, хоть на высоком уровне. Только в lua такая дебильная работа с файлами.

Автор: DarkMaster 31.3.2021, 19:23

Цитата
Только в lua такая дебильная работа с файлами.

На самом деле там проблема только одна - он делает tostring.
Вообще все эти проблемы лечит fs, насколько мне известно. Это модуль кросс-платформенный. Я часть оттуда дергал/

Автор: Madeus 31.3.2021, 22:41

Нужно добавить возврат координат найденного цвета, можно еще кол-во smile.gif

Автор: DarkMaster 31.3.2021, 23:23

Функция возвращает таблицу найденных цветов:
x y цвет1 цвет2
x y цвет1 цвет2
x y цвет1 цвет2
x y цвет1 цвет2
под цветом имеется ввиду диапазон в который попалось. А вообще спасибо, нужно переделать под:
x y найденный_цвет исходный_цвет(не диапазон)

Автор: Madeus 1.4.2021, 0:11

Проблема в том что она возвращает x y относительно координат поиска а хорошо бы возвращать реальные координаты куда можно сразу кликать.

Автор: DarkMaster 1.4.2021, 0:12

Вот для этого тесты и есть =)
Поправлю. Спасибо.

Автор: Madeus 1.4.2021, 0:17

Супер, спасибо за труды! smile.gif Ждем findimage cool.gif

Автор: DarkMaster 1.4.2021, 2:12

1)+ Переделать метод 0 для поиска по относительным координатам
2)+ Переделать возвращаемый массив в <x> <y> <найденный_цвет <искомый_цвет1> <искомый_цвет2>
3)- Добавить шаг поиска.
4)+ Добавить в images хранение высоты, ширины, длины
5)+ Использовать images для скринилки с вызовом скринилки только по адресу изображения. Поменять местами аргументы path, a.
6)+ Переделать cоlor в local c = color(x,y[,color][,deviation][,method][,abs])
7)- Буфер loadimage
8)- Финдимидж
9)+ Проблема в том что она возвращает x y относительно координат поиска а хорошо бы возвращать реальные координаты.

У меня до сих пор тяжелые думы по поводу шага поиска. Меня он всегда напрягал в плане синтаксиса - лишний мусор, ухудшение читаемости. Ускорять поиск для нахождения галочек звучит странно) В общем и целом хотелось бы услышать еще мнения по этому поводу. Надо ли оно и как это должно выглядеть в плане синтаксиса.

По поводу добавленного color. Идейно - замена color пилота c _частичной_ обратной совместимостью.
Основная задача в рамках упрощенного синтаксиса вызвать findcolor на одну точку и вернуть текущий цвет. Добавленные параметры цвета и deviation созданы с целью упрощенного использования диапазонов в условиях, например:
if color(x,y, 0xFF00FF) then
if color(x,y, "0xFF00FF-0xFF22FF") then
ибо творить писанину типа:
if color(x,y) == 0xFF00FF and color(x,y) == 0xFF01FF then
когда цвет плавает на единичку откровенно утомило.
Сравнивать же диапазоны сейчас и вовсе не реально.
Ну и собственно с введением данного чуда предлагаются к удалению все color_deviation_*
Так же хотелось бы мнение услышать.

Автор: Madeus 1.4.2021, 2:35

Цитата(DarkMaster @ 1.4.2021, 2:12) *

У меня до сих пор тяжелые думы по поводу шага поиска. Меня он всегда напрягал в плане синтаксиса - лишний мусор, ухудшение читаемости. Ускорять поиск для нахождения галочек звучит странно) В общем и целом хотелось бы услышать еще мнения по этому поводу. Надо ли оно и как это должно выглядеть в плане синтаксиса.

Я реального применения не нашел, а обычно действительно проблема была с синтаксисом, вместе с заменой координат случайно удалялся шаг.
Цитата(DarkMaster @ 1.4.2021, 2:12) *

По поводу добавленного color. Идейно - замена color пилота c _частичной_ обратной совместимостью.
Основная задача в рамках упрощенного синтаксиса вызвать findcolor на одну точку и вернуть текущий цвет. Добавленные параметры цвета и deviation созданы с целью упрощенного использования диапазонов в условиях, например:
if color(x,y, 0xFF00FF) then
if color(x,y, "0xFF00FF-0xFF22FF") then
ибо творить писанину типа:
if color(x,y) == 0xFF00FF and color(x,y) == 0xFF01FF then
когда цвет плавает на единичку откровенно утомило.
Сравнивать же диапазоны сейчас и вовсе не реально.
Ну и собственно с введением данного чуда предлагаются к удалению все color_deviation_*
Так же хотелось бы мнение услышать.

А за это однозначно лайк, сам вечно писал findcolor в одной точке)

Цитата(DarkMaster @ 1.4.2021, 2:12) *

9)+ Проблема в том что она возвращает x y относительно координат поиска а хорошо бы возвращать реальные координаты.

Все равно не то возвращает

Автор: DarkMaster 1.4.2021, 2:49

Цитата
Все равно не то возвращает

Хмм.. Вообще я не то залил, но этот фикс вроде норм был. Ща еще погоняю.

Автор: cirus 1.4.2021, 3:02

За всё время шаг поиска использовал только 1 раз. Сканировал поле для игры 3 в ряд. Учитывая, что в последнее время в играх одни и те же картинки отличаются, то такой способ уже не актуален.

Цитата
вызвать findcolor на одну точку

Код
--lua
local ffi = require("ffi")
ffi.cdef[[
int GetDC(int hWnd);
int ReleaseDC(int hWnd, int hDC);
unsigned long GetPixel(int hdc, int x, int y);
]]

function getcolor(x, y, handle)
    local HDC = ffi.C.GetDC (handle or 0)
    local color = ffi.C.GetPixel(HDC, x, y)
    ffi.C.ReleaseDC(handle or 0, HDC)
    return color
end

local result = getcolor(70, 139, workwindow())  -- координаты, окно
hint (result)

Автор: Madeus 1.4.2021, 3:04

То ли лыжы не едут то ли спать пора. В общем что у меня выходит, color вызывается с ошибкой почему не пойму attempt to call field 'color' (a nil value)

Код
 r = ext.color(1540, 509, 7440813, 1, 'r', 0)

Из того что в коде:
Код

local cc = findcolor(x, y, x, y, method, 1, deviation, deviation_type, abs_flag)

пропущен 'c' цвет короче
Код

return cc[1][3]

Возвращает только х координату

И еще я бы последовательность method deviation deviation_type оставил как в findcolor чтобы не путаться да и еще abs добавить сейчас его нет

Автор: DarkMaster 1.4.2021, 3:19

Цитата
То ли лыжы не едут то ли спать пора.

И то и другое. Спать пора - я не ту версию залил, это промежуточная была, там и не должно было нифига работать. Продолжение банкета уже завтра.

Цитата
GetPixel

Хз почему, но эта радость была тормознутее, чем снятие скриншота и вытаскивание из него отдельной точки + все проблемы с перекрытыми окнами. Насколько я знаю на гет пиксиле были построены ифы в старом синтаксисе и метод 1 в финдах.

Цитата
И еще я бы последовательность method deviation deviation_type оставил как в findcolor чтобы не путаться

И да и нет.
Я себе легко представляю запись где пристуствует некоторый deviation, ибо лениво все это раскладывать на каналы. Единичку влепил и шуршит нормально. Обратную же ситуацию не представляю. Можно закинуть метод после девиэшена в финдколоре, и вроде даже на пользу пойдет. Но тогда встает вопрос финдимиджа. Либо теряем совместимость либо финдколор и имидж будут в разных последовательностях. Как сделать правильно я хз. Если поправить именно color, то будет так же, как и с шагом:
color(x,y,c,nil,1) - и этот nil будет выбешивать.

Автор: cirus 1.4.2021, 3:33

Цитата
GetPixel
Хз почему, но эта радость была тормознутее

Цикл в 10000 итераций выполняется секунду. Сомневаюсь что кто будет проверять if'ом столько точек.
Цитата
+ все проблемы с перекрытыми окнами

Все проблемы поисков это использование Кнайтом GetWindowDC вместо GetDC.
GetDC получает клиентскую область окна, а GetWindowDC ещё и заголовок, меню, полосы прокрутки, короче всё окно. Из-за этого были проблемы со смещением координат.

Автор: Madeus 1.4.2021, 4:22

Кстати Dark, с новым getiamge ломается твой FindString) он при проверке хендла сравнивает число со строкой потому что

Код
default.window       = "workwindow"

Автор: DarkMaster 1.4.2021, 4:34

Цитата
default.window = "workwindow"

Это вообще выглядит, как ошибка. Насколько я понимаю там подразумевался workwindow(). Вообще не понимаю, как оно без указания хендла молгло работать.

Либо какие-то хвосты crystal lua и попытки вывести workwindow в переменную вместо функции (одно время в луа это была именно переменная).

на мой взгляд будет корректно вот так:
default.window = workwindow -- Метод|хендл_окна в котором происходит поиск (если указан источник изображения(source), то параметр будет проигнорирован)
и второе место для фикса:
if options.window == nil then options.window = default.window() end

Автор: DarkMaster 1.4.2021, 4:45

Пофикшены утечки памяти.

Автор: Madeus 1.4.2021, 4:48

Цитата(DarkMaster @ 1.4.2021, 4:34) *

Это вообще выглядит, как ошибка. Насколько я понимаю там подразумевался workwindow(). Вообще не понимаю, как оно без указания хендла молгло работать.

Либо какие-то хвосты crystal lua и попытки вывести workwindow в переменную вместо функции (одно время в луа это была именно переменная).

на мой взгляд будет корректно вот так:
default.window = workwindow -- Метод|хендл_окна в котором происходит поиск (если указан источник изображения(source), то параметр будет проигнорирован)
и второе место для фикса:
if options.window == nil then options.window = default.window() end

Теперь работает, ничего не ищет но с ошибкой не вылетает biggrin.gif Не ищет это из-за приложения там по хендлу черный экран smile.gif С методом 2 ищет, а вот с 0 нет.

Автор: sutra 1.4.2021, 8:29

По поводу шага. Вот внизу у вас закладки с номером текущей странички. Как узнать которая активна?
Можно узнать без шага? Конечно можно. А с шагом? По скорости однозначно быстрее. Реализация? внутри поиска + 1 оператор. Я сам использовал шаг ну раза 4. Это было в Пилоте. При нынешних скоростях возможно шаг и не актуален. Но я у себя оставил, одна строчка кода мне нисколько не мешает.

Я 100пудово могу нарисовать задачу, где без шага вам придётся лопатить массив полученных пикселей, используя кучу математики (div-ить, mod-ить и т.д), а с шагом просто найти ПЕРВЫЙ удовлетворяющий условию поиска пиксель.

А вот color ну реально не нужен. Обычное упрощение, расчитанное ну уж на совсем неопытного пользователя. Никакого реального функционала он не несёт.

Автор: sutra 1.4.2021, 9:28

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

Автор: sutra 1.4.2021, 12:02

Нашёл я причину моих мытарств, так и заставили штудировать СИ.

Код

--lua
local ffi = require("ffi")
local FILE_READ_DATA = 0x1 local FILE_WRITE_DATA = 0x2
local GENERIC_WRITE = 0x40000000
local FILE_SHARE_READ = 0x00000001  local FILE_SHARE_WRITE = 0x00000002
local CREATE_ALWAYS = 0x2
local OPEN_ALWAYS = 4
local FILE_ATTRIBUTE_NORMAL = 0x80
ffi.cdef[[
    typedef unsigned long DWORD;
    typedef DWORD *LPDWORD;
    typedef void *LPVOID;
    typedef const void *LPCVOID;
    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 saveF(path)
    local C = ffi.C
    local s,s2 = "qwertyui" , "asdfghjk"
--    local hFile = C.CreateFileA(path, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,0)
    local hFile = C.CreateFileA(path, 3, 3, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,0) -- так на мой вгляд правильнее
--    local dwWritten = ffi.new('LPDWORD')  -- а если разремарить и заремарить строку ниже - смотри дальнейшие комменты
    local dwWritten = ffi.new'DWORD[1]'
    log("pass1")
    C.WriteFile(hFile, s2, 4, dwWritten, 0)
    log("pass2")   -- хрен вам
    C.WriteFile(hFile, s, 6, dwWritten, 0)
    log("pass3")   -- хрен ещё раз
    C.CloseHandle(hFile)
    log("pass4")   -- и какого хрена ещё надо, вот и блокировка файла Пилотом
end
-- кстати если вместо нулей ставить nil, хрен вам покажет сразу сам Пилот
saveF([[C:\screen_desktop.txt]])


Cirus, Дарк - спасибо, только благодаря совместным вашим усилиям я победил эту хрень.

Автор: cirus 1.4.2021, 12:05

Цитата
Нашёл я причину моих мытарств

И в чём она? Работает и с этим ffi.new('LPDWORD') и с этим ffi.new'DWORD[1]'.

Автор: sutra 1.4.2021, 12:14

У меня не работает. Почему не знаю.

У меня выведет в лог только pass1 и файл будет 4 байта и заблокирован.

А уж почему так?? Я в этих СИ-шных типах плохо ориентируюсь.

Интересно конечно услышать ещё мнения других. Это только у меня так?? Если только у меня - тоже встаёт вопрос, а что у меня не так.

Автор: Madeus 1.4.2021, 12:45

Цитата(sutra @ 1.4.2021, 12:14) *

У меня не работает. Почему не знаю.

У меня выведет в лог только pass1 и файл будет 4 байта и заблокирован.

А уж почему так?? Я в этих СИ-шных типах плохо ориентируюсь.

Интересно конечно услышать ещё мнения других. Это только у меня так?? Если только у меня - тоже встаёт вопрос, а что у меня не так.

Запустил код вышел с LPDWORD и с DWORD, файл сохраняется никаких ошибок нет

Автор: sutra 1.4.2021, 13:20

Ошибок не будет. Файл должен быть 10 байтов. Должно 4 раза вывести в лог "pass" и файл должен удаляться не выходя из Пилота.

Автор: DarkMaster 1.4.2021, 13:31

В моей скринилке я пока шаред не врубил оно блочилось. Вообще оно как бы странно, но каких-то глобальных проблем от шареда я не вижу. Во-первых кто будет в скрины писать в несколько потоков? Ну хорошо, кто-то наваял мегакод, решил фигачить в одинаковые файлы скриншоты с разных скриптов... Ну запретим мы шаред - выбьет еррор. Легче станет?) Не думаю...

почему OPEN_ALWAYS а не CREATE_ALWAYS?

Автор: Madeus 1.4.2021, 13:34

Цитата(sutra @ 1.4.2021, 13:20) *

Ошибок не будет. Файл должен быть 10 байтов. Должно 4 раза вывести в лог "pass" и файл должен удаляться не выходя из Пилота.

Все так

Автор: DarkMaster 1.4.2021, 13:44

Цитата
А вот color ну реально не нужен. Обычное упрощение, расчитанное ну уж на совсем неопытного пользователя.

Здравствуйте, я совсем не опытный пользватель видимо) У меня половина скриптов в if color(x, y) == c then. В текущем состоянии это самая часто вызываема функция у меня. Выкатывать финдколор каждый раз имхо идея не очень. Так же в текущем состоянии пилот не может получить "красивыми" способами захвата экрана цвет пикселя.

Madeus,
Проверил еще раз по координатам и смещениям с выключенным abs_flag и методом поиска 0 - все шуршит.
Можно мне получить полные инструкции для воспроизведения. Тестами занимаюсь на пэинте - воспроизвести не получается. Подозреваю, что это как раз может быть как-то связано с менюшками и заголовками.

Madeus, что именно с финдстрингом случается? Каков синтаксис запуска? Продолжает ли он работать при включенном/выключенном abs_flag и методе 0? По хендлу? При методе 2?

Автор: sutra 1.4.2021, 14:02

Цитата
почему OPEN_ALWAYS а не CREATE_ALWAYS?

Вопрос не ко мне (код cirus). Но насколько я врубался в тему, разницы нет. Криэйт просто перезапишет любой файл однозначно. На существующем опен я не проверял.

Код
 OPEN_ALWAYS
4
Opens a file, always.
If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).

If the specified file does not exist and is a valid path to a writable location, the function creates a file and the last-error code is set to zero.


Вообще (насколько я понимаю) блочит если не закрывает дескриптор файла. Странно почему у всех по разному. Может от среды обитания зависит? Я всё использую как есть. Может у других что-то плюсом? Отладка, ... винда ... Пилот (у меня 2.41 дек. 2018).

Дарк, а что, color быстр? Я принципиально им не пользуюсь. Делаю снимок интересующей меня зоны и работаю ИСКЛЮЧИТЕЛЬНО с образом, так никогда никаких разночтений не будет. Если меня что-то не устраивает - делаю скрин, всё по моему абсолютно логично.

Автор: DarkMaster 1.4.2021, 14:04

Цитата

Вообще (насколько я понимаю) блочит если не закрывает дескриптор файла. Странно почему у всех по разному. Может от среды обитания зависит? Я всё использую как есть. Может у других что-то плюсом? Отладка, ... винда ... Пилот (у меня 2.41 дек. 2018).

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

Автор: Madeus 1.4.2021, 14:05

Цитата(sutra @ 1.4.2021, 14:02) *

Вообще (насколько я понимаю) блочит если не закрывает дескриптор файла. Странно почему у всех по разному. Может от среды обитания зависит? Я всё использую как есть. Может у других что-то плюсом? Отладка, ... винда ... Пилот (у меня 2.41 дек. 2018).

Win 10 Pro 64 лиценз со всеми обновлениями, пилот 2.41 b4

Автор: DarkMaster 1.4.2021, 14:06

Цитата
Дарк, а что, color быстр? Я принципиально им не пользуюсь.

Нет, старый - тормоз. Но каковы варианты если нужно проверить одну точку? Финдколора не было вообще, делать финдимиджи по одному пикселю каждый раз я не хочу.
Новый по скорости особо не отличается от get pixel. Пиксель тоже надо вкрутить имхо, но все по порядку. Хочу потихоньку к имиджу перейти, параллельно фиксить проблемки.

Автор: Madeus 1.4.2021, 14:07

Цитата(DarkMaster @ 1.4.2021, 13:44) *

Madeus,
Проверил еще раз по координатам и смещениям с выключенным abs_flag и методом поиска 0 - все шуршит.
Можно мне получить полные инструкции для воспроизведения. Тестами занимаюсь на пэинте - воспроизвести не получается. Подозреваю, что это как раз может быть как-то связано с менюшками и заголовками.

Тут уже запутался в чем проблема и какой версии последней с color`ом или предыдущей?
Цитата(DarkMaster @ 1.4.2021, 13:44) *

Madeus, что именно с финдстрингом случается? Каков синтаксис запуска? Продолжает ли он работать при включенном/выключенном abs_flag и методе 0? По хендлу? При методе 2?

Сейчас соберу инфу

Автор: sutra 1.4.2021, 14:21

Х. его знает. Я причину своих неудач озвучил чётко и понятно. Может кому-то это поможет. Разбираться во всех тонкостях сего процесса не имею ни времени, ни желания. Помню как в своё время чего только не изучал, и то, и сё. И кому всё это теперь нужно (кому теперь нужен ремонт магнитофонов). Плюнуть и растереть все текущие проблемы. Работает и слава богу.

Ну вот и разница. А у меня весь этот хлам висит на WIN 7.

Удивляет другое. Запись в файл существует со времён сотворения мира. Чего там может быть нового?

Попробовал новую версию Пилота - результат тот же. 4 байта и блок файла.

Автор: DarkMaster 1.4.2021, 14:31

Фиксы.

Цитата
Удивляет другое. Запись в файл существует со времён сотворения мира. Чего там может быть нового?

Потоки, виртуальные машины(луа).


Прикрепленные файлы
Прикрепленный файл  color.lua ( 34,16 килобайт ) Кол-во скачиваний: 65

Автор: Madeus 1.4.2021, 14:40

В общем что мы имеем, игра:
Cтарый getimage
Если привязаться к клиенту через workwindow или Ctrl+A то на выходе имеем черный экран. Если поставить метод 2 то ищет в fullhd если с abs то где надо.

Код

repeat
  local rs = imageToString.get(
    {
      path=path_num,
--            window=2,
      crds={810, 708, 999, 762}, --, abs=1
      accuracy=100,
      deviation=10
    }
  )
  if rs then
    r = tonumber(table.concat(rs[1]))
  end
until r
log(r)

Если не привязываться то ищет кодом выше.
--
Новый getimage = ext.getimage
Если привязаться к клиенту через workwindow или Ctrl+A то на выходе имеем черный экран включая метод 0. Если поставить метод 2 то ищет в fullhd если с abs то где надо.
Если не привязываться то черный экран включая метод 0 и код выше. Если метод 2 то ищет при этом не надо указывать abs ищет где надо.

Автор: cirus 1.4.2021, 14:50

Цитата
4 байта и блок файла.

Проверил в win7, тоже самое, не нравится LPDWORD. Видимо в win10 что-то пофиксили. Тогда использовать:
Код
local dwWritten = ffi.new('DWORD[1]')

Исправил https://forum.uokit.com/index.php?s=&showtopic=70636&view=findpost&p=440290

Автор: Madeus 1.4.2021, 14:54

Цитата(DarkMaster @ 1.4.2021, 14:31) *

Фиксы.

Color работает, и у меня видимо все теже проблемы с привязкой, без привязки цвет присылает но так же ошибку windowpos false
С координатами тоже без привязки присылает ту же ошибку хотя и работает, с привязкой ошибки нет
Код

        r = ext.findcolor(1540, 509, 1885, 963, 7110304, 0, 1, 1, 'r')
        for k,v in pairs(r) do
          for n,m in pairs(v) do
            log(n,m)
          end
        end

Вывод лога
1 94
2 309
3 7045282
4 7110304
5 7110304

Автор: sutra 1.4.2021, 15:13

Цитата
Проверил в win7, тоже самое, не нравится LPDWORD. Видимо в win10 что-то пофиксили

Спасибо cirus за поддержку, а то начал ощущать себя изгоем.

Ну теперь свернём шею всем хитросплетениям и превратностям судьбы. Пора потрудиться маленько. Всем удачи!

Автор: DarkMaster 1.4.2021, 15:16

Цитата
windowpos false

Эту ошибку выбивает при попытке преобразовать абсолютные координаты в относительные от рабочего окна. Метод 0 снимет всегда в абсолютном режиме, потом искусствено происходят корректировки. Произошли небольшие изменения синтаксиса поиска при методе 0. Если НЕ указан флаг abs, то будет искать в рамках окна. Т.е. поведение должно быть схожим с методом 2. Отсюда и происходит коллизия. Вы не указываете рабочее окно, но при этом не устанавливаете флаг abs. Метод 2 в таком случае просто "дописывал" в параметры abs. По моему мнению это не корректно. Если вы запускаете поиск в рамках рабочего окна, а рабочее окно не указано - должен быть еррор, а не подстановка не понятно чего.

Автор: Madeus 1.4.2021, 15:17

Цитата(DarkMaster @ 1.4.2021, 15:16) *

Эту ошибку выбивает при попытке преобразовать абсолютные координаты в относительные от рабочего окна. Метод 0 снимет всегда в абсолютном режиме, потом искусствено происходят корректировки. Произошли небольшие изменения синтаксиса поиска при методе 0. Если НЕ указан флаг abs, то будет искать в рамках окна. Т.е. поведение должно быть схожим с методом 2. Отсюда и происходит коллизия. Вы не указываете рабочее окно, но при этом не устанавливаете флаг abs. Метод 2 в таком случае просто "дописывал" в параметры abs. По моему мнению это не корректно. Если вы запускаете поиск в рамках рабочего окна, а рабочее окно не указано - должен быть еррор, а не подстановка не понятно чего.

Понял исправлюсь)
Тогда в колор тоже надо добавить abs флаг

Автор: DarkMaster 1.4.2021, 15:43

Цитата
Тогда в колор тоже надо добавить abs флаг

Логично =)

Я извиняюсь, но по гетмидижу и финдстригу сидел и медитировал на ваш ответ два раза по 10 минут. В чем разница поведения? Именно разное что?

Возможно там дважды происходит корректировка. Обратите внимание на строки в коде:
Цитата
5.При передаче источника поиск происходит в ошибочных координатах.
Данная проблема обусловлена багом финдимиджа.
После фикса пилота следовать инструкциям в коде после
фразы "Фикс финдимиджа"(всего три правки).

Автор: Madeus 1.4.2021, 16:02

Цитата(DarkMaster @ 1.4.2021, 15:43) *

Возможно там дважды происходит корректировка. Обратите внимание на строки в коде:

Получается что разница в методе 2 без привязки не надо указывать abs и так ищет норм что не есть правильно, помню исправлюсь, остальные не работают smile.gif А про метод 0 попробую правки внести

Там в конце опечатка orig не хватает или ext а не internal, скорее второе
Код

getimage = internal.getimage

Автор: Madeus 1.4.2021, 16:21

Цитата(Madeus @ 1.4.2021, 16:02) *

Получается что разница в методе 2 без привязки не надо указывать abs и так ищет норм что не есть правильно, помню исправлюсь, остальные не работают smile.gif А про метод 0 попробую правки внести

Там в конце опечатка orig не хватает или ext а не internal, скорее второе
Код

getimage = internal.getimage




Правки результата не дали, работает только метод 2 по факту только он и работал из-за черного экрана, и метод 0 видит тот же черный экран, что странно ведь в findcolor метод 0 работает, видимо из-за того что смещаются координаты

Автор: DarkMaster 1.4.2021, 16:45

Цитата

getimage = internal.getimage

fixed

Цитата

Получается что разница в методе 2 без привязки не надо указывать abs и так ищет норм что не есть правильно

На данный момент метод 2 полностю стандартный пилотовский. Я его не переделывал на данный момент. Поэтому поведение 0 и 2 слегка разница. Или вы говорили о разнице в методе 2 на старом гет имидже и новом гетимидже? Между чем и чем разница?

Цитата
метод 0 видит тот же черный экран, что странно ведь в findcolor метод 0 работает, видимо из-за того что смещаются координаты

Абс флаг включен? Вообще по координатам непосредственно findstring может быть несовместим с текущим getimage т.к. там были правки багов этого самого getimage имнно касательно координат. Т.е. правки багов по смещением координат сейчас применяюстя дважды, как итог там черт ногу сломит. Надо тестить будет когда все более-менее в нормальный вид придет. Сейчас нужно допилить буфер и ваять финдимидж. Когда они уже будут в связке, оба новых, то можно будет смотреть, что отвалилось и как поправить. Проблема по сути в том, что код писался под баги пилота, а теперь баги отобрали и все отвалилось. Ирония судьбы прямо-таки.

Можно попробовать сделать getimage (новый), сохранить через saveimage(новый), и передать в findstring скриншот через параметр source. Тогда там вообще getimage старый учавствовать не будет, а финдимидж получит непосредственно битовую маску.

Автор: Madeus 1.4.2021, 17:47

Цитата(DarkMaster @ 1.4.2021, 16:45) *

Можно попробовать сделать getimage (новый), сохранить через saveimage(новый), и передать в findstring скриншот через параметр source. Тогда там вообще getimage старый учавствовать не будет, а финдимидж получит непосредственно битовую маску.

Сейчас попробую
Цитата(DarkMaster @ 1.4.2021, 16:45) *

На данный момент метод 2 полностю стандартный пилотовский. Я его не переделывал на данный момент. Поэтому поведение 0 и 2 слегка разница. Или вы говорили о разнице в методе 2 на старом гет имидже и новом гетимидже? Между чем и чем разница?

Хз по чему, но при вызове нового ищет корректно без abs с методом 2, старый же ищет в фулхд. Или пилот уже троит хотя и перезапускаю его после каждой правки или хз почему, ведь он должен вызывать старый getimage при методе 2

Цитата(DarkMaster @ 1.4.2021, 16:45) *

Абс флаг включен?

Да

Автор: Madeus 1.4.2021, 18:16

То ли я что-то не так делаю то ли не помогло

Код

  repeat
    local address, width, height, length = ext.getimage(810, 708, 999, 762, 0, 'abs') --, 'abs'
    local rs = imageToString.get(
      {
        path=path_num,
--        window=2,
--        crds={810, 708, 999, 762, abs=1}, --, abs=1
        source={address, width, height},
        accuracy=100,
        deviation=10
      }
    )
    deleteimage  (address)
    if rs then
      r = tonumber(table.concat(rs[1]))
    end
  until r
  log(r)

По сути не критично, работает 2 метод и хорошо лучше findimage делать чем тут фиксить а потом опять фикстить

Автор: DarkMaster 1.4.2021, 21:22

Цитата
лучше findimage делать чем тут фиксить а потом опять фикстить

Его и делаю. На самом деле прям в удовольствие. Сижу спокойно пишу в одну харю, мне еще и подсказки дают и тесты гоняют. Что еще нужно? smile.gif Давно хотел что-то в удовольствие сам что-нибудь покодить. Когда делаешь, не то, что говорят, а то, что хочешь и когда никто не подсовывает тебе новые куски творения под которые нужно подстраиваться. Сегодня обнов не планирую - пишу финд, там все-таки нужно прилично катать.

Автор: cirus 2.4.2021, 3:28

Добавил ещё один вариант сохранения изображения используя fwrite вместо WriteFile.
https://forum.uokit.com/index.php?s=&showtopic=70636&view=findpost&p=440290

Автор: DarkMaster 2.4.2021, 4:14

Цитата
Добавил ещё один вариант сохранения изображения используя fwrite вместо WriteFile.

А он разве внутри не использует WriteFile?

Автор: DarkMaster 2.4.2021, 4:40

Что по скорости?

// Пока лепил имидж потестил разный тип deviation сохраняя картинки который получаются после применения deviation - достаточно любопытно выглядит. 'a' - чистый brightness, 'r' вроде как контрастность получается =)

Автор: cirus 2.4.2021, 11:50

Цитата
А он разве внутри не использует WriteFile?

Скорее всего использует. fopen, fwrite, fclose примерно на 10% выполняются дольше, чем вызов winapi функций.

Автор: DarkMaster 3.4.2021, 5:23

Ето. Я не забил) Я наворотил smile.gif Процесс идет.

Автор: DarkMaster 3.4.2021, 9:52

Цук вроде буфер доделал... Сколько он у меня крови попил... Зато теперь 1 конвретация фул хд изображения по методу r занимает 36мс, 1000 конвертаций 64мс. В общем генерация изображений по a, r готовы. Осталось накатать таблицу на s (там не изображения) ну и перебор сгенерированных изображений.

Автор: Madeus 3.4.2021, 13:41

Супер! Ждем! Ну я уж точно! laugh.gif

Автор: DarkMaster 4.4.2021, 9:33

По сути первый запуск. Ну почти первый. Вроде даже нашло.
Синтаксис почти аналогичен старом финдколору, надо будет чуть-чуть дошаманить.
r = clr.findimage(0, 0, 1919, 1079, {[[image\fi.bmp]]}, 264916 , 90, 1, 1, 'r')
r = clr.findimage(0, 0, 1919, 1079, {[[image\fi.bmp]]}, [[image\scr.bmp]], 90, 1, 1, 'r')

папки пока не жрет, кэш работает на загруженное через getimage и на загруженное финдколором в качестве _искомого_ изображения. Т.е. в примере выше scr.bmp будет автоматически удален из кэша. Где искать. Если искать по ранее загруженному в память изображению, то источник(где искать), должен быть передан в виде таблицы {a, w, h, l}. Таблицы, а не списка. Т.е.:
local t = {1,2,3,4} - правильно
а не
local t = {["a"]=1, ....} -- так не писать. ошибка
если данное изображение получено через getimage, либо "осело в кэше", то w, h, l при вызове можно не указывать. Если захотелось пофантазировать и кодом сгенерировать некоторую картинку, то можно ее отправить указав полный синтаксис {a, w, h, l}.

s метод в финдимидже еще не делал, в колоре не помню, но вроде рабочий.
строгое сравнение тоже не делал еще.

В целом код не прилизан, манов толком нет и т.д. Надо все причесывать и очень сильно.


Прикрепленные файлы
Прикрепленный файл  color.lua ( 54,25 килобайт ) Кол-во скачиваний: 74

Автор: cirus 4.4.2021, 11:36

В ext.findimage нет объявления переменной t.
Если картинка по указанному пути не найдена, то выдаст ошибку.

Автор: Madeus 4.4.2021, 16:03

Цитата(cirus @ 4.4.2021, 11:36) *

В ext.findimage нет объявления переменной t.
Если картинка по указанному пути не найдена, то выдаст ошибку.

это опять тесты скорости были) закоментить вывод логов надо сразу

Цитата(DarkMaster @ 4.4.2021, 9:33) *

По сути первый запуск. Ну почти первый. Вроде даже нашло.

При указании deviation_type 'a' ошибка
Код

color1.lua:928: attempt to perform arithmetic on global 'h' (a nil value)

При указании метода 2 ошибка
Код

color1.lua:1153: attempt to index a nil value



А вот со скоростью явно что-то не так:
Игра ищем неизвестное кол-во кнопок старый фаинд ищет так deviation 5 и 15
Код

test = findimage (1151, 377, 1364, 899, {path .. 'Button' .. '.bmp'}, 2, 75, -1, 5)
Speed = 0.055999999996857

test = findimage (1151, 377, 1364, 899, {path .. 'Button' .. '.bmp'}, 2, 75, -1, 15)
Speed = 3.7170000000042

Новый фаинд ищет так
Код

test = ext.findimage (1151, 377, 1364, 899, {path .. 'Button' .. '.bmp'}, 0, 75, -1, 20, 'r', 'abs')
Speed = 0.22499999999127


Самое долгое в браузере Firefox ищем кнопку логина
Старый фаинд
Код

test = findimage (769, 733, 1121, 843, {path .. 'Login' .. '.bmp'}, 2, 80, 1, 1)
Speed = 0.039999999993597

test = findimage (0, 0, 1919, 1079, {path .. 'Login' .. '.bmp'}, 2, 80, 1, 1)
Speed = 0.13400000000547


Новый фаинд
Код

test = ext.findimage (769, 733, 1121, 843, {path .. 'Login' .. '.bmp'}, 0, 80, 1, 1, 'r')
Speed = 0.7780000000057

test = ext.findimage (0, 0, 1919, 1079, {path .. 'Login' .. '.bmp'}, 0, 80, 1, 1, 'r')
Speed = 14.098000000013

14 секунд в фулхд smile.gif

А ну и координаты он все равно возвращает относительно начала координат поиска. Если искать в фул хд вернет то что надо, если указать зону конкретную то вернет относительно ее начала и килкнуть по найденой картинке невозможно. И еще он возвращает 16 найденых картинок что-ли #test == 16, хотя count 1. Старый 1 находит.

Автор: DarkMaster 4.4.2021, 19:05

Цитата
это опять тесты скорости были) закоментить вывод логов надо сразу

не закомментил именно для тестов ибо были сомнения в производительности. Не до конца понимаю почему и как так получилось, хотя таких диких цифр у меня не было. Искал 9*9 каринку где-то 0.36 на фул хд. Надо смотреть где тянет вниз. Там, к сожалению, не все так очевидно бывает. Тут прямо место для Cockney, который обязан просто сказать, что мы занимаемся херней и для этого есть другие языки и компиляторы. Мне сложно со всем этим не согласится, равно, как и исключить при оценке выбора языка тот момент, что финды были на протяжении жизни поилота самыми изменяемыми функциями и поддержка при нескольких языках, на мой взягляд, будет неоправдано усложенена. Проще сделать нормально один раз, потом по мере необходимости докручивать какие-нибудь фишки вроде новых типов поиска.
Цитата
При указании deviation_type 'a' ошибка

Тестовый вариант включен был. Расскоментил рабочий, закомментил тестовый. Второй блок реальный был.
Цитата
При указании метода 2 ошибка

А вот это на самом деле очень странно ибо getimage я создавал так, что он должен в итоге поставлять одинаковый результат по части формата. Адрес в number, w, h, l и в кэш закидывать. Посмотрю.

Цитата
А ну и координаты он все равно возвращает относительно начала координат поиска.

То был колор, это имидж) Подручу, там 2 строчки из имиджа скопировать.

Цитата
И еще он возвращает 16 найденых картинок что-ли #test == 16, хотя count 1. Старый 1 находит.

Забыл.

Ща фиксить буду. Больше всего откровенно пугает скорость. Sutra ты там тут?) В чем косяк fi_compare?

Автор: Madeus 4.4.2021, 20:15

Цитата(DarkMaster @ 4.4.2021, 19:05) *

То был колор, это имидж) Подручу, там 2 строчки из имиджа скопировать.

Прогнал еще раз findcolor твой, в фул хд возвращает корректно, в указанной области возвращает относительно начала координат поиска, версия последняя с фиксами, где нет еще findimage`a

Автор: Cockney 4.4.2021, 20:49

Цитата(DarkMaster @ 4.4.2021, 19:05) *

Тут прямо место для Cockney, который обязан просто сказать, что мы занимаемся херней и для этого есть другие языки и компиляторы.



А что я ? Молчу-молчу. Вы наверняка лучше знаете и понимаете как реализовать потенциал lua. Могу только заметить, что лучше сначала собрать базу аномалий и фиксить уже после того как у всех стабильно будет все находиться. В конце концов jit смотрит на возможности процессора при компиляции, и кто знает что он генерирует.

Вообще, не стоит наказывать самих себя) Заведите гит репозиторий, отводите ветки и смотрите что лучше/хуже работает. Так вы не будете ходить по одним и тем же граблям. Как минимум коллективно.

Автор: cirus 4.4.2021, 20:56

DarkMaster, а ты уверен что у пилотного findimage поиск просто перебором? С маленькими картинками может и будет быстро искать, но чуть побольше уже сомнения. К примеру возьмём картинку 40*25, 1000 пикселей, при 50% точности нужно проверить половину, т. е. 500 пикселей чтобы понять что в проверяемом месте картинка не найдётся. Т. е. для FullHD 1920*1080*500 больше миллиарда пикселей надо проверить.

Автор: DarkMaster 4.4.2021, 22:10

Цитата
DarkMaster, а ты уверен что у пилотного findimage поиск просто перебором?

Я уверн. Уверен, что нет =) Мне известна, как минимум одна оптимизация, но на тестовых изображениях я специально создаю условия, чтобы она не работала. В целом она достаточно сомнительна для меня на самом деле. Если быть точнее, то она будет работать в плюс только в определенных ситуациях.

Автор: DarkMaster 4.4.2021, 23:11

Хз. Переделал на ffi.cast по адресам - разницы особо не увидел... Недопонимаю в чем проблема.

Автор: DarkMaster 5.4.2021, 4:34

Вобщем у меня тесты на фул хд и искомом изображении 46*41. Искомое изображение в одном экземпляре в правом нижнем углу, т.е. находится в самом конце. При 90% точности время поиска занимает 10 секунд. Во время тестов выяснелось следующее... Каждый новый счетчик добавляет примерно 0.5 сек, само условие со вложенной проверкой на достаточность количесво пикселей занимет 9 секунд. Если выкинуть проверку на достаточность пикселей, то время выполнения сокращается на 5 секунд.
Что со всем этим делать пока не понятно... Если выкинуть весь хлам и оставить один счетчик, то выполняет мгновенно...

Автор: Madeus 5.4.2021, 5:03

Цитата(DarkMaster @ 5.4.2021, 4:34) *

Вобщем у меня тесты на фул хд и искомом изображении 46*41. Искомое изображение в одном экземпляре в правом нижнем углу, т.е. находится в самом конце. При 90% точности время поиска занимает 10 секунд. Во время тестов выяснелось следующее... Каждый новый счетчик добавляет примерно 0.5 сек, само условие со вложенной проверкой на достаточность количесво пикселей занимет 9 секунд. Если выкинуть проверку на достаточность пикселей, то время выполнения сокращается на 5 секунд.
Что со всем этим делать пока не понятно... Если выкинуть весь хлам и оставить один счетчик, то выполняет мгновенно...

Ни че не понял, но очень интересно biggrin.gif
У меня кол-во секунд не складывается...

Автор: sutra 5.4.2021, 12:13

Всем Гуд! Дарк, у меня не было времени тестить новые фишки. Да и надоели мне все эти тесты, я собственно говоря - практик, мне интересно смотреть методы, которые вы используете, чтобы потом использовать их самому. Я не большой сторонник МЕГА проектов. На мой взгляд лучше сделать несколько функций (old, new или вообще дать функциям новые имена), чем всё запихивать в одну. На мой взгляд ты торопишься с "похоронами" имеющихся финдов. Давай для начала сделаем loadimage, чтобы хоть какая-то была работоспособность, а уж потом хоронить всё старое. Да и saveimage я бы доработал. Ну для себя точно буду переделывать. Мне нужно, чтобы сохранялся в файл не ВЕСЬ образ, а его конкретная зона. Ну вот пример практика ... Сделал образ ... поискал одно ... поискал второе ... а на третьем, что-то пошло не так и мне было бы намного удобнее сохранить в файл именно эту область, чтобы потом не шариться в редакторе высчитывая координаты.

Автор: sutra 5.4.2021, 12:45

По поводу финдимиджа стандартного (текущего). Я давным-давно от него отказался. Я не знаю причин, но там просто убийственные тормоза, если увеличиваешь deviation. У моего варианта нет никаких зависимостей от этого параметра. У меня зависимость только от параметра accuracy и это понятно, потому что поиск выполняется до превышения "ошибок". Я использую 3 параметра: погрешность (deviation) нужна при малых значениях RGB, точность (% отклонения цвета) и аккуратность (похожесть картинки) если например часть картинки перекрыта другой картинкой.

И как уже говорил, я использую свой (кривой, нестандартный, но очень эффективный) метод. Я формирую (перед тем как пихнуть её имиджу) искомую картинку по своему усмотрению. Может искаться и фон и не фон, количество искомых условий не ограничено (каждое условие может иметь свои параметры отклонений), на скорость никак не влияет. В частности при таком подходе поиск символов (цифр и букв) становится намного проще, точнее и быстрее.

Проще говоря создаётся матрица {X,Y,R1,R2,G1,G2,B1,B2} и ищется эта самая матрица. Если подсунута не матрица, имидж создаёт её сам, а если нет использует подсунутую. В большинстве случаев, особенно критичных к скорости, используются уже именно готовые матрицы. Они загружаются у меня при старте из бинарного файла и всё летает на ура.

Для меня задача №1 loadimage. Без него никак не обновиться по полной программе. Думаю сам проковыряюсь долго, хотя вроде общие принципы мне понятны.

Чтобы было понятнее про матрицу. Ну вот например поиск цифр 7 и 2 осуществляется поиском (сравнением) всего 2-х пикселей. У семёрки 2 крайних верхних, а у двойки 2 крайних нижних, надеюсь понятно объяснил.

Автор: DarkMaster 5.4.2021, 12:46

Цитата
На мой взгляд лучше сделать несколько функций

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

Я его собирался переписывать, но после финдов ввиду того, что текущий типа и так работает, а при включенном буфере вообще пофигу, как он работает. Грузится 1 раз, больше к виниту обращений не будет.
Цитата
Да и saveimage я бы доработал. Ну для себя точно буду переделывать. Мне нужно, чтобы сохранялся в файл не ВЕСЬ образ, а его конкретная зона.

Обрезалки так же планировались. Просто на мой взгляд гораздо разумнее сделать основной функционал и потом потихоньку его расширять, тем более, что достаточно очевидны направления расширения.
Цитата
И как уже говорил, я использую свой (кривой, нестандартный, но очень эффективный) метод. Я формирую (перед тем как пихнуть её имиджу) искомую картинку по своему усмотрению. Может искаться и фон и не фон, количество искомых условий не ограничено, на скорость никак не влияет. В частности при таком подходе поиск символов (цифр и букв) становится намного проще, точнее и быстрее.

Я верю в то, что это очень эффективно. Вопрос в том какое количество людей сможет этим пользоваться =) У меня есть свой взгляд на то, как вообще подобный код должен формироваться. По большому счету лично мое мнение, что картинка, которая будет использоваться в поиске должна генерироваться, области задавться руками, по дефолту с нулевым разбегом от позиции изображения и т.д. Человек должен достаточно просто и быстро создавать подобные вещи, а многие аспекты обработать в ручную нереальный по затратам времени труд. У меня стоит 3 задачи:
1) Закрытие текущих багов и необоснованных тормозов.
2) Создание кода, который может свободно использоваться в последующем для создания скриптов. И использоваться не каким-то гуру и не через Ж.
3) Перевод на луа и после создания определенной основы (вектор задать, если угодно), получить свободно расширяющиеся модули пользовательские с централизованным распространением. Это одна из причин, почему пока я хочу кодить один. Садиться и писать подробные тз у меня желания нет, получить франкенштейна, когда каждый писал, как хотел я тоже не имею желания.

Автор: DarkMaster 5.4.2021, 12:57

Цитата

Проще говоря создаётся матрица {X,Y,R1,R2,G1,G2,B1,B2} и ищется эта самая матрица. Если подсунута не матрица, имидж создаёт её сам, а если нет использует подсунутую. В большинстве случаев, особенно критичных к скорости, используются уже именно готовые матрицы. Они загружаются у меня при старте из бинарного файла и всё летает на ура.

У меня предельно похожая схема. При загрузке файла он падает в буфер, при вызове финдимиджа с некоторым девиэйшеном генерируется 2 массива по своей структуре являющиеся корректными bitmap. В одном минимально допустимые значения во втором максимально допустимые. В итоге идейно все должно заканчиваться сличением трех массивов-битмапов "некторый скрин", "максимальный искомый", "минимально искомый". Ну и дальше идет попиксельно сравнение
min<скрин<max
Эти сгенерированные изображения с отклонениями так же падают в буфер и повторно не создаются для тех же параметров deviation.
Цитата
Для меня задача №1 loadimage. Без него никак не обновиться по полной программе. Думаю сам проковыряюсь долго, хотя вроде общие принципы мне понятны.

Я в нем проблемы вообще не вижу. Честно. Проще сделать "обрезалку", которую все равно делать. И в эту обрезалку загонять адрес битмапа в оперативке, а не на винте. С винта лоад дергат вполне нормально.
Цитата
Чтобы было понятнее про матрицу. Ну вот например поиск цифр 7 и 2 осуществляется поиском (сравнением) всего 2-х пикселей. У семёрки 2 крайних верхних, а у двойки 2 крайних нижних, надеюсь понятно объяснил.

Это вопрос создания подобных изображений. Сравнивать то их чем-то все равно нужно, а создают зачастую ломти до 25*25 пикслей (в среднем). 2 пикселя и текущий вариант быстро сравнит. Видимо придется разгр<вырезано анти-матом> твой код сравнения. Честно скажу - писал больше с нуля ибо чужой код и не самый простой кусок. Учитывая полученные проблемы с производительностью надо смотреть, что у тебя получается.

Можешь на своем текущем коде в твоей реализации сделать поиск изображения 40*40 в фул хд и точностью 90%? Хочу понять насколько разница в производительности. На моем очень древнем железе получалось около 830млн пикселей в секунду.

Автор: sutra 5.4.2021, 13:09

Цитата
Вопрос в том какое количество людей сможет этим пользоваться

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

А почему сейчас про это вспомнил. Сам принцип (внутренней обработки, не для пользователя), ну он ведь в принципе такой же как у тебя. Задаются просто границы поиска RGB, и потом элементарный перебор с минимумом математики внутри.

Хорошо, потетстю на твоей картинке с мишенями и скажу что и как. Надо тока найти её, вроде ничего не удалял.

Автор: DarkMaster 5.4.2021, 13:17

Мое текущее безобразие.

По поводу не надо было... Одно дело надо, другое возможность. Иногда кушать еще надо( Возможность появилась, я занимаюсь. И ты сразу, как почуял)


Прикрепленные файлы
Прикрепленный файл  fi.bmp ( 90 байт ) Кол-во скачиваний: 75
Прикрепленный файл  fi_large.bmp ( 5,66 килобайт ) Кол-во скачиваний: 46
Прикрепленный файл  scr.bmp ( 5,93 мегабайт ) Кол-во скачиваний: 55

Автор: sutra 5.4.2021, 13:18

Хотя чего там тестить. Просто кину кусок кода который собственно и ищет, всё по минимуму. Чисто для примера, сорри без комментов, с кривыми именами переменных. Сделано давно и просто лень было облагородить.

Код

      while y<ye do
        while x<xe do
          for i=1,pic[0][0]do     -- pic - матрица
            n=ins+pic[i][6]
            if a[n][v1]<pic[i][v1]or a[n][v1]>pic[i][V1]or a[n][v2]<pic[i][v2]or a[n][v2]>pic[i][V2]or a[n][v3]<pic[i][v3]or a[n][v3]>pic[i][V3]then
              j=j+1 if j>sim then  sr=false  break  end  -- sim аккуратность
            end
          end
          if sr then  af[k],k={x,y/wi},k+1  if k>numf then  return k-1,af  end  end
          x,sr,ins,j=x+1,true,ins+1,0
        end
        x,y,ins=0,y+wi,ins+pic[0][2]-1
      end

Автор: DarkMaster 5.4.2021, 13:26

Цитата
Хотя чего там тестить.

Ну вот чтобы я не пытался криво втулить свои картинки непонятно куда. Т.е. если там выпадет по времени работы 10 секунд, то смысла все это разгр<вырезано анти-матом> не вижу. Хотя у меня подозрения все есть, что твой код отработает шустро. Если нет, то все равно напишу, просто не так, как хотел. Я _ОЧЕНЬ_ не хочу тулить dll'ки. Возможно именно под сравнение трех массивов придется, но это лично для меня будет большой фейл.

Автор: sutra 5.4.2021, 13:49

Из кода видно, что чем меньше матрица pic[0][0], тем быстрее будет поиск. НАМНОГО быстрее если лопатить большие зоны. В этом вся и фишка. Хотите скорость - надо напрячь мозги. Лень, ну тогда будет медленнее. Каждый выберет для себя компромисс сам.

Из твоего bmp-шника не совсем понятно как искать. Насколько уникальна картинка, можно искать по красному, можно по зелёному. СМЫСЛ в чём?? Чтобы увеличить скорость, надо понять уникальность картинки. И если например такого ярко красного пикселя нет нигде, кроме как в этой картинке, то можно искать тупо по одному пикселю, а ещё тупее - найти его колором.

Соврал, по одному не получится, а вот по ДВУМ - легко.

И повторюсь. Я могу искать не только значимые пиксели (скажем красные), но и фон. Например посередине от этих двух красных однозначно присутствует светлый пиксель (скажем RGB данного пикселя все значения не менее 100) или например бесцветный пиксель значения RGB отличаются друг от друга не более, чем на 30 единиц. Нет никаких ограничений для фантазии юзера.

Ещё раз повторюсь. Если мне например не нужна скорость, ничего я не делаю, лень ... никаких условий ... задал погрешность и всё. А при желании можно задавать сколь угодно сложно комбинированные условия поиска (по которым и будет создана матрица поиска).

Автор: Madeus 5.4.2021, 13:51

Цитата(sutra @ 5.4.2021, 13:49) *

Ещё раз повторюсь. Если мне например не нужна скорость, ничего я не делаю, лень ... никаких условий ... задал погрешность и всё. А при желании можно задавать сколь угодно сложно комбинированные условия поиска (по которым и будет создана матрица поиска).

Проблема в том, что большинство юзеров не задумывается что и как искать, ищут то что видят, и как они это видят, и тут встает вопрос быстродействия "для всех".

Автор: cirus 5.4.2021, 13:51

Цитата
Я _ОЧЕНЬ_ не хочу тулить dll'ки.

Что это даст? Луа и си сравнимы по скорости. Другие языки явно не ускорят поиск в разы.
fi_large.bmp ищется 4 секунды на i7-7700K 4.20GHz.
Так что надо менять алгоритм поиска.

Автор: sutra 5.4.2021, 13:56

И вот чтобы искать такие бесцветные пиксели и нужны RED-BLUE , RED-GREEN , GREEN-BLUE

Цитата
и тут встает вопрос быстродействия "для всех".

Это в принципе невозможно. Скорость и так повысили getimage-м , она вполне себе приемлема и не сопоставима с Пилотовской. Скорость моего алгоритма зависит только от юзера и от мощности его машинки. НО всегда есть маневр, о какой уравниловке может идти речь, её в природе не существует.

Автор: DarkMaster 5.4.2021, 13:58

Цитата
Луа и си сравнимы по скорости

Есть у меня сомнения. В любом случае количество пикселей необходимых для проверки не меняется. Каким образом можно проверить не 189 пикселей, а 2 и понять, что это не наше изображение? Старый финд же как-то ищет. Оптимизации накрываются кстати медным тазом, если искать не одно изображение, а все. Никуда не денешься - проверять придется все.

sutra, пожалуйста, прогони через свой алгоритм мои изображения. Девиэйшн можешь поставить минимальынй(но не нулевой) - оттенки идентичные. Какая скорость нахождения изображения fi_large внутри scr? accuaricy 10% - это важно, т.к. вместо 1- пикслей, ему нужно будет обработать не менее 189 на каждую возможную координату в пределах scr.

Автор: sutra 5.4.2021, 14:00

Хорошо, сейчас попробую, протестирую. Мне по барабану какой девиэйшн, всё будет искаться одинаково. Спазу скажу, ни о каких секундах не может быть и речи. Сейчас попробую.

Автор: DarkMaster 5.4.2021, 14:08

Цитата
Что это даст? Луа и си сравнимы по скорости. Другие языки явно не ускорят поиск в разы.
fi_large.bmp ищется 4 секунды на i7-7700K 4.20GHz.

Мой финд: 9.8 сек (только этап сравнения).
Старый финд с методом забора изображения "2": 0.106
Искал 100 изображений, так, что перелопатил он там все. Ну либо старый финд откровенно врет с accuracy либо все-таки где-то косяк. Я склоняюсь ко второму варианту.

Автор: sutra 5.4.2021, 14:17

Код

--lua
local path=[[A:\МММ]]                                      -- Объявление + инициализация каталога сохранения файлов создаваемых скриптом
local f=require[[Scripts\LUA\fun]]
local p,w,h,l=loadimage([[A:\scr.bmp]])
local PIC=f.CreateFindArray({{[[A:\fi_large.bmp]],r=0,acc=90}},1920)
local k,m
local t1=os.clock()
for i=1,10 do
  k,m=f.FindImage(p,l,0,0,1919,1079,PIC,1)
end
local t2=os.clock()
deleteimage(p)
deleteimage(p1)
log(k)
log(m[1][1],m[1][2])  -- 1874 1039
log(t2-t1)            -- 0.109 сек

Автор: cirus 5.4.2021, 14:17

Брать с искомой картинки первый пиксель и искать его на скрине, если нашёлся, то уже искать саму картинку в этом месте. Если точность не 100%, то брать второй пиксель и снова искать и т. д. пока хватает точности. Для fi_large.bmp этот способ точно будет быстро искать. По сути сначала вызываем findcolor и уже потом используем findimage в найденных координатах.

Автор: sutra 5.4.2021, 14:22

Это искались ВСЕ голубые пиксели (r=0)

Код чисто для примера, запускать не надо (для новичков), без модуля понятно работать не будет.

Автор: DarkMaster 5.4.2021, 14:29

Цитата
Брать с искомой картинки первый пиксель и искать его на скрине, если нашёлся, то уже искать саму картинку в этом месте. Если точность не 100%, то брать второй пиксель и снова искать и т. д. пока хватает точности. Для fi_large.bmp этот способ точно будет быстро искать. По сути сначала вызываем findcolor и уже потом используем findimage в найденных координатах.

Это все будет работать пока ты не будешь искать картинок больше, чем есть на экране. Именно поэтому я и вбиваю 100 картинок, когда там она одна. Чтобы увидеть скорость без оптимизаций.

10 прогонов. хе. Кодом поделишься? Буду искать где проблема...

Еще один маленький вопрос. Не затруднит ли перед непосредственно ифом который весь массив лопатит поставить счетчик? Ну тупо чтобы знать сколько раз этот иф был вызван(вне зависимости от результата истина/ложь).

Цитата
Это искались ВСЕ голубые пиксели (r=0)

Эмм... Это финдколор получается) А именно имиджем по 3 каналам. Так сказать старой тяжелой артиллерией.

Автор: sutra 5.4.2021, 14:32

Погоди Дарк. Надо смотреть. Что-то я не доглядел. Не глядя пихнул. Там оранжевые - это что? Паразитные что-ли? или фон?

Автор: DarkMaster 5.4.2021, 14:44

Цитата
Погоди Дарк. Надо смотреть. Что-то я не доглядел. Не глядя пихнул. Там оранжевые - это что? Паразитные что-ли? или фон?

Вообще фон серый. Есть два изображения которые я ищу: маленькое fi.bmp - на большом их 4 штуки по углам, второе изображение большо: fi_large.bmp и оно там только в правом нижнем углу. Смысл теста в том, чтобы без хитрожопостей типа 1 канал, исключение фона и т.д. найти fi_large. Левый верхних пиксель фоном не является. Все грубо, базово.

Автор: sutra 5.4.2021, 14:51

Понял, сорри, не то делал.

local PIC=f.CreateFindArray({{[[A:\fi_large.bmp]],r=0,R=254,fgr=true}},1920) -- 1,154 (10 итераций) всё кроме оранжа

--local PIC=f.CreateFindArray({{[[A:\fi_large.bmp]],r=0,fgr=true}},1920) -- 0.120 (10 итераций)


В любом случае быстрее выходит.

Так large отличается от того что на скрине. Там оранжевый кант в левом углу совсем другой.

Автор: DarkMaster 5.4.2021, 14:51

Я не спал уже хз сколько, ухожу на отдых в уверенности, что завтра будет понимание и прогресс =)

Автор: cirus 5.4.2021, 14:52

Цитата
Брать с искомой картинки первый пиксель и искать его на скрине, если нашёлся, то уже искать саму картинку в этом месте. Если точность не 100%, то брать второй пиксель и снова искать и т. д. пока хватает точности. Для fi_large.bmp этот способ точно будет быстро искать. По сути сначала вызываем findcolor и уже потом используем findimage в найденных координатах.
Это все будет работать пока ты не будешь искать картинок больше, чем есть на экране. Именно поэтому я и вбиваю 100 картинок, когда там она одна. Чтобы увидеть скорость без оптимизаций.

Какая разница? Берём первый пиксель, даже если найдется несколько тысяч таких пикселей на скрине, проверить наличие картинок в этих местах вообще ничего не стоит по времени. Если конечно не искать белый цвет в блокноте. В любом случае не миллиард пикселей проверять. Да, при очень низкой точности искать тоже будет долго, но этого и не требуется.
Может есть ещё какие-то варианты.
Цитата
В любом случае быстрее выходит.

sutra, где в твоём поиске параметр точности поиска? Поиск при 100% точности в любом алгоритме будет искать быстро.

Автор: DarkMaster 5.4.2021, 14:58

Цитата
Там оранжевый кант в левом углу совсем другой.

Какой нафиг другой? Я копипастил о,О

Цитата
Какая разница?

Огромная разница. Это очень сильно ускорит поиск в случае, когда нужно найти 1 изображение и пофигу где. Когда нужно найти все изображения или, скажем, просто в упорядоченном виде, то начинаются проблемы. Упорядоченность дает конечно не полный перебор, но не без развлечений. А если тебе надо найти 100 изображений, а там только одно, то как ты сможешь исключить позицию, где 188 пикселей подряд идут ошибочные, а потом все нужные? В итоге тот же полный перебор будет.
Цитата

В любом случае быстрее выходит.

Дай код а... можно в приват...

Автор: sutra 5.4.2021, 15:02

Цитата
sutra, где в твоём поиске параметр точности поиска? Поиск при 100% точности в любом алгоритме будет искать быстро.

В моём случае НЕТ РАЗНИЦЫ. Сравнений ровно столько же. дал погрешность +- 20 - результат тот же.

Автор: cirus 5.4.2021, 15:03

Цитата
188 пикселей подряд идут ошибочные, а потом все нужные? В итоге тот же полный перебор будет.

Полный перебор двух миллиардов пикселей (1920*1080) или нескольких тысяч возможных мест? Как бы есть разница.

Автор: sutra 5.4.2021, 15:19

10 итераций (+- 20 по всем каналам) 1,2 сек. При работе в параллельном тесте, не прерывая работу моего другого скрипта.

Это large

И это искался и фон.

local PIC=f.CreateFindArray({{[[A:\fi_large.bmp]],r=195,g=195,dev=20}},1920) -- 0,109

Это задал фон - быстрее сразу в 10 раз.


В общем, однозначно пользуюсь своим, проверенным.

И что сложного в моих скриптах, на мой взгляд всё не так и сложно.

4 маленьких картинки нашлись за 0,13 сек (10 итераций цикла)

Как и говорил ранее - главный тормоз getimage . Теперь уже в прошлом!

Автор: sutra 5.4.2021, 15:42

Так что разбирайся Дарк, что-то у тебя не так. Кусок кода я дал, сравнение всех пикселей картинки с предельными значениями, собственно ничего тут такого нет. Быстро даже не используя моих хитростей.

Естественно всё это СИ-шные массивы.

Я знаю где тормоз. У меня матрица содержит сведения ГДЕ находится искомый пиксель. Я не лопачу всё подряд, а смотрю конкретную позицию. Вот за счёт этого и скорость. Дарю идею - пользуйся. Хотя ещё полтора года назад дарил.

n=ins+pic[i][6] -- это индекс в битовой маске - это ключ скорости.

Если будет надо могу дать весь код, там наверняка много кривизны, но мысль понятна.

Автор: sutra 5.4.2021, 16:36

Держи всё целиком. Лепил из кусков модуля, мог и накосячить, но вроде работает.
Только пути к файлам переправьте.

код
Код

--lua
local ffi=require "ffi"
local rmem=ffi.cast
log "clear" log "mode compact"                                                 -->Очистка лога. Установка компактного режима отображения лог информации
local function ImageToArray(ad,l,x1,y1,x2,y2,de)
  local h,w=y2-y1+1,x2-x1+1
  local k=h*w
  local d,a=ad+3*x1+l*y1,ffi.new("uint8_t["..k.."][3]")
  if w*3==l then
      for i=0,k-1 do  a[i][0],a[i][1],a[i][2],d=rmem("unsigned char*",d)[0],rmem("unsigned char*",d)[1],rmem("unsigned char*",d)[2],d+3  end
    else
      local i,s=0,l-w*3
      for y=1,h do
        for x=0,w-1 do  a[i][0],a[i][1],a[i][2],d,i=rmem("unsigned char*",d)[0],rmem("unsigned char*",d)[1],rmem("unsigned char*",d)[2],d+3,i+1  end
        d=d+s
      end
  end
  if de then  deleteimage(ad)  end  return a
end----------------------------------------------------------------------------
local function CreateFindArray(co,WI,deladr)
  local k,z,b,g,r,p,d,ad,w,h,l=0,0,ffi.new("uint8_t"),ffi.new("uint8_t"),ffi.new("uint8_t"),ffi.new("uint8_t[6]"),ffi.new("int16_t[6]")
  if type(co[1][1])=="string" then
      ad,w,h,l=loadimage(co[1][1])
    else  ad,w,h,l=co[1][1],co[1][2],co[1][3],co[1][4]
  end
  local ar,f,x,y,i,R,G,B,P,D,RG,RB,GB,ba,ga,ra,acc,v,zat=ffi.new("uint32_t[?][7]",w*h+1),ffi.new("uint16_t["..h.."]["..w.."]")
  if co[1].zat then  zat=co[1].zat/100  else  zat=0  end
  while z<#co do
   & nbsp;z,R,G,B,P,D,RG,RB,GB,y,x,i=z+1,true,true,true,true,true,true,true,true,0,0,
ad
    if co[z].r then  P,R,p[0],p[1]=false,false,co[z].r,co[z].R or co[z].r  end
    if co[z].g then  P,G,p[2],p[3]=false,false,co[z].g,co[z].G or co[z].g  end
    if co[z].b then  P,B,p[4],p[5]=false,false,co[z].b,co[z].B or co[z].b  end
    if co[z].rg then  D,RG,d[0],d[1]=false,false,co[z].rg,co[z].RG or 255  end
    if co[z].rb then  D,RB,d[2],d[3]=false,false,co[z].rb,co[z].RB or 255  end
    if co[z].gb then  D,GB,d[4],d[5]=false,false,co[z].gb,co[z].GB or 255  end
    if co[z].acc and co[z].acc<100 then  acc=(100-co[z].acc)/100  else  acc=0  end
    if co[z].dev then  v=co[z].dev  else  v=0  end
    if co[z].fgr then
        while y<h do
          while x<w do
            if f[y][x]==0 then
              b,g,r=rmem("unsigned char*",i)[0],rmem("unsigned char*",i)[1],rmem("unsigned char*",i)[2]
              if(D or((RG or r-g>=d[0]and r-g<=d[1])and(RB or r-b>=d[2]and r-b<=d[3])and(GB or g-b>=d[4]and g-b<=d[5])))and(P or((R or r>=p[0]and r<=p[1])and(G or g>=p[2]and g<=p[3])and(B or b>=p[4]and b<=p[5])))then
                if zat>0 then  b,g,r=math.floor(b-b*zat),math.floor(g-g*zat),math.floor(r-r*zat)  end
                k,ba,ga,ra,f[y][x]=k+1,b*acc+v,g*acc+v,r*acc+v,1
                ar[k]={math.max(0,math.floor(b-ba)),math.max(0,math.floor(g-ga)),math.max(0,math.floor(r-ra)),math.min(255,math.ceil(b+ba)),math.min(255,math.ceil(g+ga)),math.min(255,math.ceil(r+ra)),x+y*WI}
              end
            end
            i,x=i+3,x+1
          end
          y=y+1  i,x=ad+y*l,0
        end
      else
        while y<h do
          while x<w do
            if f[y][x]==0 then
              b,g,r=rmem("unsigned char*",i)[0],rmem("unsigned char*",i)[1],rmem("unsigned char*",i)[2]
              if not((D or((RG or r-g>=d[0]and r-g<=d[1])and(RB or r-b>=d[2]and r-b<=d[3])and(GB or g-b>=d[4]and g-b<=d[5])))and(P or((R or r>=p[0]and r<=p[1])and(G or g>=p[2]and g<=p[3])and(B or b>=p[4]and b<=p[5]))))then
                if zat>0 then  b,g,r=math.floor(b-b*zat),math.floor(g-g*zat),math.floor(r-r*zat)  end
                k,ba,ga,ra,f[y][x]=k+1,b*acc+v,g*acc+v,r*acc+v,1
                ar[k]={math.max(0,math.floor(b-ba)),math.max(0,math.floor(g-ga)),math.max(0,math.floor(r-ra)),math.min(255,math.ceil(b+ba)),math.min(255,math.ceil(g+ga)),math.min(255,math.ceil(r+ra)),x+y*WI}
              end
            end
            i,x=i+3,x+1
          end
          y=y+1  i,x=ad+y*l,0
        end
    end
  end
  if not(deladr)then  deleteimage(ad)  end  ar[0]={k,WI,w,h}  return ar
end --------------------------------------------------------------------------------
local function FindImage(a,lG,x1,y1,x2,y2,pic,numf,sim,v1,v2,v3) -- v1-v3 очерёдность анализа калов (0-blue, 1-green, 2-red) (нафиг не нужны, по логике должен быть прирост)
  local af,k,wi={},1
  if type(a)~="cdata" then
    if not(a)then
        x1,y1,x2,y2,a,wi,wi,lG=0,0,x2-x1,y2-y1,getimage(x1,y1,x2,y2,lG)
        a=ImageToArray(a,lG,x1,y1,x2,y2,true)
      else
        a=ImageToArray(a,lG,x1,y1,x2,y2)
    end
  end
  wi,numf,sim,v1,v2,v3=x2-x1+1,numf or 1,sim or 100,v1 or 2,v2 or 1,v3 or 0
  if type(pic)~="cdata" then  pic=CreateFindArray(pic,wi)
    elseif pic[0][1]~=x2-x1+1 then
--      log(pic[0][1],x2-x1+1)  -- оставлено информирование для себя, т.к. это лишний тормоз
      for i=1,pic[0][0]do     -- пересчёт позиций
        local ys=math.floor(pic[i][6]/pic[0][1])
        pic[i][6]=pic[i][6]-ys*pic[0][1]+ys*wi
      end
      pic[0][1]=x2-x1+1
  end
  if pic[0][0]==0 then  return 0,nil  end
  local sr,x,y,ins,xe,ye,V1,V2,V3,n=true,0,0,0,wi+1-pic[0][2],(y2+2-pic[0][3]-y1)*wi,v1+3,v2+3,v3+3
  if sim<100 then
      sim=pic[0][0]*0.01*(100-sim)  local j=0
      while y<ye do
        while x<xe do
          for i=1,pic[0][0]do     -- pic - матрица
            n=ins+pic[i][6]
            if a[n][v1]<pic[i][v1]or a[n][v1]>pic[i][V1]or a[n][v2]<pic[i][v2]or a[n][v2]>pic[i][V2]or a[n][v3]<pic[i][v3]or a[n][v3]>pic[i][V3]then
              j=j+1 if j>sim then  sr=false  break  end  -- sim аккуратность
            end
          end
          if sr then  af[k],k={x,y/wi},k+1  if k>numf then  return k-1,af  end  end
          x,sr,ins,j=x+1,true,ins+1,0
        end
        x,y,ins=0,y+wi,ins+pic[0][2]-1
      end
    else
      while y<ye do
        while x<xe do
          for i=1,pic[0][0]do
            n=ins+pic[i][6]
            if a[n][v1]<pic[i][v1]or a[n][v1]>pic[i][V1]or a[n][v2]<pic[i][v2]or a[n][v2]>pic[i][V2]or a[n][v3]<pic[i][v3]or a[n][v3]>pic[i][V3]then
              sr=false  break
            end
          end
          if sr then  af[k],k={x,y/wi},k+1  if k>numf then  return k-1,af  end  end
          x,sr,ins=x+1,true,ins+1
        end
        x,y,ins=0,y+wi,ins+pic[0][2]-1
      end
  end
  return k-1,af
end --------------------------------------------------------------------------------

local p,w,h,l=loadimage([[A:\scr.bmp]])
--local PIC=CreateFindArray({{[[A:\fi_large.bmp]],r=195,g=195,dev=20}},1920)
local PIC=CreateFindArray({{[[A:\fi.bmp]],r=195,g=195,dev=20}},1920)

local k,m
local t1=os.clock()
for i=1,10 do
  k,m=FindImage(p,l,0,0,1919,1079,PIC,5)
end
local t2=os.clock()
deleteimage(p)
log(k)
log(m[k][1],m[k][2])  -- 1874 1039
log(t2-t1)            -- 0.109


Дарк, сделай сворачивалку, забыл как.

Описание функций лень писать. Если у кого интерес будет - распишу всё подробно. Делал то для себя. Сам и то не всё помню, чего нагородил. Много можно упростить, так как особого прироста скорости нет.

Никакого контроля ошибок нет, так что аккуратнее.

Автор: sutra 5.4.2021, 17:05

z,R,G,B,P,D,RG,RB,GB,y,x,i=z+1,true,true,true,true,true,true,true,true,0,0,ad

Вот так должна выглядеть строка в функции CreateFindArray

которая следует за началом цикла
while z<#co do

почему-то некорректно копируется код. Так что правьте эту строку руками.

Ну или держите файл как есть.


Прикрепленные файлы
Прикрепленный файл  FindImage.lua ( 6,65 килобайт ) Кол-во скачиваний: 57

Автор: cirus 6.4.2021, 3:02

DarkMaster, когда давно, по-моему ты и задавал вопрос как определяется точность, с которой была найдена картинка. Кнайт подтвердил мой предположение что точность определяется после поиска. Из чего можно сделать вывод что всегда ищется вся картинка. Т. е. в цикле нет проверки что дальше нет смысла искать, только счётчик сколько совпало пикселей. А уже после поиска проверка достаточно ли совпало пикселей.
Точность поиска не влияет на скорость в пилотном findimage, т. е. в нём явно нет тупого перебора всех пикселей. Также sutra писал что поиск тормозит при большом отклонении в цвете, что вполне логично, находится много пикселей и надо во всех проверить есть ли в этих местах картинка.
По сути надо как минимум 2 варианта поиска. Тот что сейчас сильно зависит от точности, но почти не зависит от отклонения в цвете. Предложенный мной вариант не зависит от точности поиска, но зависит от отклонения в цвете.

Автор: DarkMaster 6.4.2021, 4:05

Цитата
точность определяется после поиска

Может это и было, но в последней версии это не так. Ну либо точность не вычисляется до конца. Если задать точность 50% и закинуть для поиска картинку полностью совпадающую с изображением, то получится точность 50%.
На самом деле у меня появилась идея, как это все было сделано. Если я правильно прикинул, то там все-таки полный перебор, но перебор несколько иной. У меня перебор избыточный. И я понял (ну догадываюсь) откуда такое дикое падение производительности в старом финде при девийшине и его фишечки. По сути это объясняет все. Единственно, что мне на самом деле очень интересно касательно старого алгоритма, то если его повторить на чистом си, с указателями и все такое, то какова будет разница в скорости. Имеется ввиду fi_compare() Прям задело.

Автор: DarkMaster 6.4.2021, 5:20

Кстати 1920*1080 ~= 2 миллионов, а не миллиардов :-P

Автор: sutra 6.4.2021, 9:03

Друзья, вы меня конечно извините, но вы не над тем голову ломаете. Давайте по фактам.
Чем больше диапазон поиска (не важно какими параметрами он расширен), тем дольше будет поиск и ничего тут ускорить нельзя. Дарк, твой пример для тестов не годится. Чтобы реально нагрузить поиск, весь экран должен быть напичкан похожими картинками. И вот для такого случая я бы привёл вам реальные примеры использования моего финда, увеличивая скорость поиска в разы.
Далее ... Одну картинку найти - это не проблема. Давайте исходить из того, что нам придётся искать их сотню. Я подправил немножко имеющийся тест, подсовывая для поиска в функцию не адрес, а СИ-шный массив. Скорость выросла ровно в 2 раза и составила 0,5 секунды.

Код
local p,w,h,l=loadimage([[A:\scr.bmp]])
local PIC=CreateFindArray({{[[A:\fi_large.bmp]],r=0,R=255,fgr=true,dev=20}},1920)
--local PIC=CreateFindArray({{[[A:\fi.bmp]],r=195,g=195,dev=20}},1920)
local SCR=ImageToArray(p,l,0,0,1919,1079,1)
local k,m
local t1=os.clock()
for i=1,100 do
  k,m=FindImage(SCR,l,0,0,1919,1079,PIC,5)
end
local t2=os.clock()
--deleteimage(p)
log(k)                -- 1
log(m[k][1],m[k][2])  -- 1874 1039
log(t2-t1)            -- 0.514

В принципе не так плохо. Но если тест будет не синтетическим, а реальным, скорость резко упадёт.
В чём моя фишка, в том, что я могу задавать последовательность поиска. То есть, первым условием я ставлю условие {r=0} - это искать голубые пиксели, так как их намного меньше. А вторым условием - искать все остальные {r=0,R=255} - это если учитывать все пиксели, в том числе и пиксели фона.
Моя матрица содержит X,Y искомых пикселей - вот за счёт чего достигается скорость. Ты повнимательнее Дарк посмотри код, уверен, что улучшишь свой алгоритм, хотя меня мой вполне устраивает. Если там сделать проверки на ошибки, можно запросто запускать в оборот.

Автор: DarkMaster 6.4.2021, 9:10

Сколько if'ов в секунду у тебя выполняется?

Цитата
Дарк, твой пример для тестов не годится

Он был создан исключительно для тестов краев. После первой жалобы на скорость был создан большой квадрат.

Автор: sutra 6.4.2021, 9:18

Цитата
Сколько if'ов в секунду у тебя выполняется?

Ну сколько ... примерно 1920*1080. Чего там искать на пустом экране? Я ж говорю тест слабый.

На практике, у любой картинки есть своя уникальность: сверкающий меч или шлем; опознавательный знак и т.д.. И у меня есть возможность задать приоритетные пиксели, с которых и начинается поиск, и в подавляющем большинстве случаев идёт переход на следующую итерацию поиска.

Автор: sutra 6.4.2021, 9:39

Единственное, что можно было бы добавить в мой (ну и в твой наверное тоже) алгоритм - шаг поиска картинки. Но мне это не надо, а всем остальным, как я понимаю мои идеи не интересны. Живой пример. Cirus кстати его давал. Нужно собрать ресурсы (золотишко) с домиков. Я могу легко и однозначно найти золото синтетической картинкой из 3-х пикселей. Но будет искаться несколько картинок на одном фрагменте золота, а вот если после первой найденной, делать смещение на n-е количество пикселей - этого можно избежать. Короче, найти можно абсолютно всё (ну почти всё), надо всё-таки двигаться в технологичном направлении. При подробном описании возможностей функции, с конкретными примерами, даже не самые продвинутые юзеры (хотя Пилот не для тупых) будут в восторге.

А тебе Дарк - это всё внедрить просто сам бог велел. Это же твой хлеб - реальные деньги. Нарисовал юзеру скрипт, который всех кладёт на лопатки - юзер тебя отблагодарит. Ну кроме меня, меня вам не уложить. biggrin.gif

Если смог чем-то помочь, с вас новый loadimage , а то боюсь, что я с моим знанием СИ такого наваяю...

Автор: DarkMaster 6.4.2021, 10:01

Цитата
Ну сколько ... примерно 1920*1080. Чего там искать на пустом экране?

При поиске картинки 46*41 с допустимым отсутствием 10% пикселей - это примерно 367 999 254 проверок. Это много. Поэтому я еще раз спрашиваю сколько ифов в секунду выполняет у тебя твой код?
Цитата
Если смог чем-то помочь, с вас новый loadimage , а то боюсь, что я с моим знанием СИ такого наваяю...

Накидаю, как раз нужно мозги от финда отвлечь. Что-то тут не то, надо принципиально понять, что. 100% правильных идей (прям совсем правильных) мне приходили не за компом - код только мешает.

Автор: sutra 6.4.2021, 10:06

Цитата
с допустимым отсутствием 10% пикселей

Нет там никакого отсутствия. Я искал с отклонением +-20. 1 951 885 if-ов.

Автор: sutra 6.4.2021, 10:39

Пропуск пикселей - это задача для мозгов. Тут ты никак не победишь тормоз. Глупо, наивно и бессмысленно искать с потерей 10% картинку такого размера. Пропуск нужен не для точности, для этого есть диапазон, а для поиска перекрытых друг другом картинок, как правило - это небольшие объекты. А для такой огромной картинки о каких 10% может идти речь? Предположим, на ней есть паразитное изображение, вопрос, какого оно размера? 10%? Не о том вы думаете. Короче, тут вопросы не к алгоритму, а к подходу к проблеме юзера и даже не заморачивайся на этом. ПРОПУСК - это край, и надо 100 раз почесать репу, прежде чем решить, а мне это надо???

Если пропускать, то обязательно применять МОЙ подход к делу.

Код
local p,w,h,l=loadimage([[A:\scr.bmp]])
local PIC=CreateFindArray({{[[A:\fi_large.bmp]],r=0,fgr=true,dev=20}},1920)
--local PIC=CreateFindArray({{[[A:\fi.bmp]],r=195,g=195,dev=20}},1920)
local SCR=ImageToArray(p,l,0,0,1919,1079,1)
local k,m,n
local t1=os.clock()
for i=1,10 do
  k,m,n=FindImage(SCR,l,0,0,1919,1079,PIC,5,90)  -- 90%
end
local t2=os.clock()
deleteimage(p)
log(k)                -- 1
log(m[k][1],m[k][2])  -- 1874 1039
log(t2-t1)            -- 0.842
log(n)                -- 13650074 ифов


А если искать все пиксели - даже пробовать не буду - лень ждать.

Я даже представить себе не могу, что может быть таким "паразитным" объектом. Гигантский прицел?

Если действительно возникает проблема, что юниты в куче друг на друге, только скрупулезный анализ изображений этих юнитов и грамотный подход к созданию эталонных картинок может решить вопрос скорости - это как раз ТВОЙ хлеб. А ежели кажный хмырь будет уметь это делать???

Короче. Резюме. Скорость - это не только алгоритм. Вся база на текущий момент практически идеальна. Всё равно - главное - это: размер картинки; зона (зоны) поиска; интеллектуальное слежение за объектами (что они как грибы? то тут, то там?); грамотное создание картинок; грамотное задание условий поиска. Да - это нельзя достичь тупо написав findimage, надобно включать голову, без этого никак. Всё, больше даже не желаю обсуждать эту тему.

Автор: DarkMaster 6.4.2021, 10:49

Цитата
Нет там никакого отсутствия.

Так я поэтому и спрашивал. Deviation в моем методе поиска вообще никак не влияет на скорость. Хоть 1 хоть 255. У меня при твоих условиях делает 100 прогонов на 1.66 секунды. Поставь точность на 100% и девийшн в любое положение.
Цитата
А для такой огромной картинки о каких 10% может идти речь? Предположим, на ней есть паразитное изображение, вопрос, какого оно размера? 10%?

Понятно, что тестовое изображение дубовое. Оно создано исключительно для того, чтобы давать нарузку и служить _стресс_ тестом.
Цитата
Не о том вы думаете.

Проблема в том, что старый финдимидж все это лопатит несопостовимо быстрее. Т.е. если делать дубовый поиск влоб перебором, то у меня это 10 секунд, у старого финда - 0.1 секунды. Т.е. разницы в 100 раз. И это вопрос непосредственно алгоритма и его реализации. Т.е. при тех же входных данный дикий проигрышь. Ты говоришь, что скорость это основное - ну так проигрышь в 100 раз это нифига не правильно, да я понимаю, что это можно начать компенсировать правильным подбором изображения, но почему не привести сразу в порядок функцию непосредственно поиска? Это же логично. Хочется еще - образай картинки, тебе уже дали ускорение в 100 раз. Короче надо разбираться до конца. У меня есть идеи, хочу поднапрячь человека чтобы мне "правильных" изображений нагенерил. Правильных в том плане, чтобы они содержали критичные ситуации для разных типов подхода поиска. Тогда смогу оправдать или развеять мои теории.

Нет смысла убирать якорь в гетимидже и делать новый якорь в финдах =) Надо доводить до ума.

Автор: sutra 6.4.2021, 11:07

Цитата
Проблема в том, что старый финдимидж все это лопатит несопостовимо быстрее

Что-то я такого не замечал. Для всех моих случаев, при моих условиях поиска, он был в 1000 раз медленнее. Надо будет как-нить потестить старый. Но я ж говорю, там даже на deviation такой якорь наступает, а это в 100 раз важнее пропуска, тем более, старый финд он чего ищет? Там конкретно просит фон, а всё остальное ищет - это вообще лично для меня неприемлемо. Там какой-то свой алгоритм, возможно есть там что-то, но только я сильно сомневаюсь, что там есть что-то супергениальное.

Что может найти старый финд? Кучу юнитов друг на друге - НЕТ? Прицел - НЕТ? Ничего подобного он не может, вот и весь мой вердикт.

Cirus, спасибо!!! Я однозначно перехожу на твою технологию. Напишете loadimage - спасибо. Нет, ну поковыряюсь сам, чего-нибудь однозначно придумаю.

Мне просто лень тестить старый. Дарк, попробуй на своём тесте нарисуй прицел и посмотри что и как будет искаться.

Автор: DarkMaster 6.4.2021, 11:10

Цитата
Напишете loadimage - спасибо

Я уже сказал, что накидаю.

Автор: sutra 6.4.2021, 11:17

Я не знаю как тестировать старый, только если искать изображение в редакторе. А как задать цикл? Если там всё время подгружается файл? Без цикла, будет неточный тест.

Если хватит воли и смогу победить свою лень, если потестирую старый - отпишусь.

Автор: DarkMaster 6.4.2021, 11:30

Цитата
Я не знаю как тестировать старый, только если искать изображение в редакторе.

я так тестирую.
Цитата
А как задать цикл? Если там всё время подгружается файл? Без цикла, будет неточный тест.

Можно использовать мой гетимидж.

По сути тест старого для меня лично особо не критичен. Понять бы как допрыгнуть)

Автор: cirus 6.4.2021, 12:02

Цитата
Напишете loadimage

А чем пилотный не подходит? Адрес возвращает, больше от него ничего и не нужно.

Автор: sutra 6.4.2021, 12:31

Цитата
А чем пилотный не подходит?

А что-то у меня не получалось это совместить с новым getimage.

Потестил старый финд. При малых deviation он делает нам "козью рожу". Наш алгоритм пропуска действительно неэффективен. Мы лопатим каждую позицию, прежде чем понять, удовлетворяет нас результат или нет. Это неверный подход. Есть у меня идея. В двух словах, скорее всего, надо искать попиксельно, а не целиком всю картинку, сначала сверяем 1-ый пиксель по всей зоне, потом 2-ой и т.д. Во всяком случае - это первое, что мне пришло в голову.

Вопрос только в том, что мне пока непонятно, либо весь алгоритм сразу переделывать, либо возможно требуется компромисс. То есть, например, если погрешность задана ну скажем менее 25% - новый алгоритм, если больше оставить старый. Надо пробовать и смотреть.

Грубо говоря, в моём алгоритме цикл 3-го уровня, делать циклом первого. Примерно так. Возможно я ошибаюсь - это спонтанное решение.

Уверен, у Кнайта именно такая реализация. И именно поэтому тормоза начинаются и увеличиваются именно с увеличением погрешности, т.к. растут циклы 2-го и 3-го уровня, а 1-й цикл (количество искомых пикселей неизменен). Я почти уверен, что я прав. В чудеса и сверхспособности я не верю.

Запросто переделаю, прямо сейчас. Всё именно так, как я думаю. Если не трогать погрешность, и менять процент accurace - это никак не влияет на скорость у Кнайта. Вывод очевиден, всё очень даже просто.

Автор: sutra 6.4.2021, 13:56

Дарк, переделал алгоритм. Скорость поиска - нуль. Чем больше % пропуска, тем даже быстрее. Сейчас некогда, через часик освобожусь, слегка подправлю код и выложу результаты. Пока ясно одно, наши алгоритмы работают.

Автор: sutra 6.4.2021, 16:23

Поторопился я, всё не так просто. Да, если искать только одну картинку, то вроде всё понятно, а вот если несколько, тут у меня пока не получается. Надо думать, но наверное уже не сегодня.

Автор: sutra 6.4.2021, 18:26

Прошу прощения, я ОЧЕНЬ сильно затупил. Думаю что в поставленной задаче ничего ускорить нельзя. Я вспомнил. Я даже вроде об этом спрашивал в теме про Пилот. Я нашёл в своё время косяки у стандартного финда и вроде (точно уж не помню) это и было как раз при поиске нескольких картинок, когда они накладывались друг на друга. В общем чего-то он не находил. Я не знаю какой там алгоритм, возможно какие-то методы исключения зон из последующих проверок. Короче господа, вы уж как хотите, а я буду использовать свой алгоритм, проверенный, абсолютно понятный, с предсказуемым результатом. А пропускать 250 пикселей на тестируемой картинке, я не вижу этому никакого практического применения. Ещё раз сорри за дезинформацию.

Автор: DarkMaster 7.4.2021, 12:24

Прикручена функция
-- ext.image_copy
-- Копирует часть изображения.
-- Допустимый синтаксис:
-- <address>,<x1>,<y1>,<x2>,<y2> -- если изображение загружено новым getimage
-- <{address, width}>,<x1>,<y1>,<x2>,<y2>
-- <{address=val, width=val}>,<x1>,<y1>,<x2>,<y2>
-- <{a=val, w=val}>,<x1>,<y1>,<x2>,<y2>

Sutra, что касательно сохранения части изображения. Резалка универсальная, ничего не мешает ей пользоваться перед сохранением. Тем не менее saveimage модифицирован для сохранения именно куска.

Важно понимать, что image_copy создает копию части битовой маски, соответственно - это полноценный кусок памяти загнанный в буфер. Необходимо удалять изображения из памяти (deleteimage), если они более не использются. В случае если происходит вызов saveimage с сохранением части изображения, то image_copy и deleteimage будут вызваны скрыто, дополнительного удаления не потребуется.

Что не так с loadimage и что ты от него хочешь? Просто альтернативу на луа?


Прикрепленные файлы
Прикрепленный файл  color.lua ( 61,8 килобайт ) Кол-во скачиваний: 71

Автор: sutra 7.4.2021, 12:59

Спасибо. Да я сам не знаю чего я хочу. Сам дотункаю, так что не парьтесь. Надо будет наверное просто глянуть чем отличается write от read. Я же для себя ковыряю. Всё переделал. Мне больше чем ХД не нужно. Сделал 2 глобальных массива CI[1] и CI[2], сразу выделил им по ХД, в функцию даю параметр в какой массив битовую маску получать и всех делов. Я так подумал, десятки раз в секунду у меня происходит захват экрана, зачем мне абсолютно ненужные действия, кроме тормозов никаких плюсов. Почесал конечно репу, пока не вспомнил про твой минус высоты, а то всё с задницы начиналось. Я ведь по факту и получал по адресу этот самый массив, короче убрал лишнее действие.

Автор: sutra 7.4.2021, 13:13

Вот только пока не определился с чем лучше работать с 32 битами или по старинке с 24.
И думаю всё про Кнайтовский алгоритм. Прямо даже зацепило. Наверное как-то построчно идёт анализ, уж очень хитро. Хотя, как и говорил, есть у меня подозрения на косяки. Я давно экспериментировал с ним, могу конечно ошибаться, как говорится не в первой. Но вроде как финд косячил именно на смешении картинок. Помню я нарисовал что-то типа забора, ну в общем, куча повторяющихся элементов, получалось как бы картинка на картинке. И на этом большом заборе искал его куски, естественно их находится много, и вроде как он неверные координаты выдавал. Хотя конечно интересно бы глянуть на алгоритм.
НО. Зацикливаться на нём точно не надо. Минусов в старом в разы больше. По моему опыту, мне вот как раз не надо делать пропуски. Всё с точностью до наоборот. Мне надо распознавать объекты в разных состояниях. А пропуски будут игнорить эти различия. Повторюсь, не вижу никакого применения, кроме наложения объектов, но как правило - это небольшие объекты и всё работает быстро. Во всяком случае я не видел особых тормозов, когда распознал ВСЕ твои мишени, всё равно, с новым имиджем получается однозначно быстрее старого финда.

Автор: sutra 7.4.2021, 13:36

Спасибо вам друзья огромное!! Лично мне всего хватает и скорости и функционала. Вспоминаю, как извращался с Пилотом на родном ему языке - ужас. А нынешние технологии, для меня это просто прорыв.

А тебе Дарк, как раз стоит подумать про забор. Очень интересные там могут получаться комбинации. Почему я и говорил про шаг поиска и при поиске картинок. И хоть у меня пока таких проблем не возникало, но, во всяком случае практическое применение этому я вижу.

Конечно если вы вытащите алгоритм Кнайта - это будет круто, но я сколько не думал, не могу понять, как можно найти все элементы забора, которые присутствуют с минимальным смещением и по горизонтали и по вертикали, кроме как перебирать ВСЕ варианты.

Автор: sutra 7.4.2021, 13:47

У меня остался один вопрос, может подскажете. Как резвенько скопировать часть СИ-шного массива в другой СИ-шный массив. В Паскале была функция move, которая позволяла это делать. Выделять память как я понимаю надо использовать malloc , удалять free.

Автор: cirus 7.4.2021, 14:04

Цитата
Как резвенько скопировать часть СИ-шного массива в другой СИ-шный массив.

Код
--lua
local ffi = require("ffi")
ffi.cdef[[
    void RtlMoveMemory(void*  Destination, void const *Source, unsigned int Length);
]]

log 'clear' log 'mode compact'

local array = ffi.new('int[10]', {0, 10, 20, 30, 40, 50, 60, 70, 80, 90})   -- массив на 10 элементов

local array2 = ffi.new('int[5]')  -- массив на 5 элементов
ffi.C.RtlMoveMemory(array2, array+2, 20)  -- скопировать 20 байт, начиная с 3го элемента

log(array2[0])
log(array2[1])

Автор: sutra 7.4.2021, 14:19

Спасибо Cirus! Скоро буду СИ-шником. biggrin.gif

Автор: Cockney 7.4.2021, 14:55

Это не Си. Смотрите memcpy() функцию.

Автор: sutra 7.4.2021, 16:00

Цитата
Смотрите memcpy() функцию

Посмотрел, спасибо. А как к скрипту присобачить?
И вообще я вроде затупил. Привык по-старинке ... выделил память ... освободил память.
А тут я так понял само всё чистится, как только выходишь из зоны видимости?

Код
    void memcpy(void*  Destination, void const *Source, unsigned int Length);

Правильно объявил? Вроде работает.

Автор: Cockney 7.4.2021, 16:04

Цитата(sutra @ 7.4.2021, 16:00) *

Посмотрел, спасибо. А как к скрипту присобачить?
И вообще я вроде затупил. Привык по-старинке ... выделил память ... освободил память.
А тут я так понял само всё чистится, как только выходишь из зоны видимости?



Ну если явно вызывается malloc(), то нужно вызывать free(), если просто происходит ffi.cast, то ничего делать не требуется.

Автор: sutra 7.4.2021, 16:05

Не, malloc - это указатель делает, нахрен не нужен. Делаю new

Автор: Cockney 7.4.2021, 16:05

Цитата(sutra @ 7.4.2021, 16:00) *

Код
    void memcpy(void*  Destination, void const *Source, unsigned int Length);

Правильно объявил? Вроде работает.



Да.

Автор: sutra 7.4.2021, 16:17

Код
if true then
  local arr={}
  for i=1,10 do
    arr[i]=ffi.new('unsigned char[?]',100000)
    ffi.C.memcpy(arr[i],CI[1],4)
--    ffi.C.RtlMoveMemory(arr[i],CI[1],4)
    log(arr[i][0])
    wait(1000)
  end
  log(arr[3][1])
end


То есть по окончании оператора if память будет очищена? Всё верно понимаю? Специально по 100 кило выделял, смотрел загрузку памяти, вроде всё почистилось само.

Как я понял memcpy самый быстрый способ, так как нет никакого контроля границ диапазонов, по сравнению скажем с memmove.

Автор: Cockney 7.4.2021, 16:26

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

https://stackoverflow.com/questions/2963898/faster-alternative-to-memcpy

Автор: sutra 7.4.2021, 16:44

Точно, не чистится, сделал задержку после ифа , посмотрел на память - не очистило. Тогда так нельзя. Тогда что? только через malloc?

Инглиш не для меня. Я в советское время учился и мы считали, что этот буржуйский язык нам никогда не пригодится.

Мне просто показалось, что rmem всё-таки подтормаживает процесс, почему я и использую СИ-шные массивы. Как то всё уж больно хитро. Вот раньше, как просто у меня было. getmem p-адрес p^[25] указатель на конкретные данные freemem - свободен. А тут кругом препятствия.

Задача то простая. В цикле (скажем 1 сек.) ... Сканирую экран ... поиск объектов ... хотелось бы получить копию битовой маски (небольшая) ... если всё штатно освободить память, если нет сохранить все кадры в файлы и освободить память и это всё в одном большом глобальном (почти вечном) цикле.

Автор: DarkMaster 7.4.2021, 16:48

Цитата
только через malloc?

Во первых если ты будешь использовать указатели, то только malloc иначе сборщик мусора просто уничтожит данные. Он не сопоставляет указатели. Т.е. если были какие-то данные local и больше к ним не планируется доступ напрямую, то сборщику будет глубоко пофиг на то, что где-то висит указатель и через этот указатель ты будешь дергать данные.
Выделять память настоятельно рекоменду через gc.
local bitmap_address = ffi.gc(C.malloc(bitmap_size), C.free)
чтобы сборщик знал, как прибить.
Если память используешь активно, то придется либо явно вызывать free либо collectgarbage() - иначе можешь обожраться памяти (временно).

bitmap_address = nil
дальше сборщик сделает свое дело

Автор: Cockney 7.4.2021, 16:49

В чем смысл так часто очищать память ? На производительность это хуже только повлияет.

Автор: sutra 7.4.2021, 16:51

Да понял я уже это Дарк, спасибо. Кстати в дремучем Паскале на каждое new было своё dispose. Жаль, что здесь нет такого.

Цитата
В чем смысл так часто очищать память ? На производительность это хуже только повлияет

Мне производительность нужна, когда я осуществляю поиск сканируя экран. А когда уже всё случилось, мне эти доли секунды глубоко по барабану.

Автор: DarkMaster 7.4.2021, 16:52

Цитата
В чем смысл так часто очищать память ? На производительность это хуже только повлияет.

1 битовая маска фул хд чуть меньше 6 метров. Если скринить в одно и то же место - проблем нет. Если ты дергаешь постоянно в новые места, то будут проблемы. Зависит от задачи по большому счету.

Автор: sutra 7.4.2021, 17:03

Это требуется только в критичных (надеюсь пока критичных) ситуациях. Но крутится всё в одном глобальном цикле. Никакой памяти не хватит если её не чистить.

Дарк, не будет проблем. Очистка памяти не дольше её выделения. Всю жизнь все так программировали.

Цитата
Зависит от задачи по большому счету.
Я же сказал НЕБОЛЬШАЯ. У меня ничего не бывает большим. А задача - "найти и обезвредить". biggrin.gif

А вообще, вы наверное правы. Экономить память - древняя дурная привычка, когда вся оперативка составляла 512 КБ. Наверное проще выделить 1000 массивов и пускай висят в памяти, благо этого дерьма хватает.

Автор: Cockney 7.4.2021, 17:52

Цитата(DarkMaster @ 7.4.2021, 16:52) *

1 битовая маска фул хд чуть меньше 6 метров. Если скринить в одно и то же место - проблем нет. Если ты дергаешь постоянно в новые места, то будут проблемы. Зависит от задачи по большому счету.



Это понятно. Имелись в виду пулы таких масок. Скажем пул на серию из 24 изображений. Перебрали все - занулили через memset(). Вопрос больше к выделению памяти. Как она выделяется ? Несколько подходов есть. Можно через виндовые функции по типу HeapAlloc(), а можно через менеджер памяти, который дает язык (Pascal в этом и удобен). В первом случае просить каждый раз - очень медленно. Второй - быстро, но память по факту в систему не возвращается. Это уже так, конечно, придирки, но вроде тут о скорости речь.

Цитата(sutra @ 7.4.2021, 16:51) *

Мне производительность нужна, когда я осуществляю поиск сканируя экран. А когда уже всё случилось, мне эти доли секунды глубоко по барабану.



А после поиска еще поиск....

Автор: sutra 8.4.2021, 12:11

Цитата
А после поиска еще поиск....

Ну конечно. Только для следующего поиска совершенно не критична погрешность пусть даже в 1 сотую секунды. Я не думаю что очистка памяти осуществляется дольше. Мы тут с десятыми долями боремся и даже с секундами при использовании параметра похожесть картинки. Мне вполне хватало скорости, кроме одного уникального случая, про который я спросил и с которого и началась вся эта заворуха.
Спасибо всем за инфу. Я для себя принял решение. При старте скрипта, для всей зоны видимости, просто объявлю сразу 2-3 сотни СИ-шных массивов нужного мне объёма памяти, вот и всё. Памяти полно, чего её жалеть.

А при работе с памятью, собственно нужно (желательно) соблюдать одно условие, по принципу работы стека. То есть исключать сумбур, а последовательно выделять и освобождать память, дабы исключить фрагментацию памяти.

Автор: sutra 8.4.2021, 12:47

Я всё думал про алгоритм Кнайта. Вы почитайте историю развития. Что там написано - ищет по уникальному для картинки цвету (абсолютно прагматичный подход), но он не должен быть ничем перекрыт. Погрешность прикручена уже позже. Думаю весь секрет скорости зарыт здесь. Возможно осуществляется и какой-то анализ экрана (то на чём ищется картинка).
А вот тут и возникает вопрос, что лучше, иметь скорость при использовании похожести, но не иметь 100% достоверности или наоборот. Практические аспекты использования имиджа в разных ситуациях я уже приводил. Принцип Кнайта очень даже хорош. Я пытаюсь делать собственно то же самое - ЗАДАТЬ УНИКАЛЬНОСТЬ для искомой картинки, но совершенно иным способом.

Автор: sutra 8.4.2021, 13:02

Я проделал небольшой эксперимент. Запоганил слегка тестовый экран Дарка на 10%. Фоном принял то же пиксель, что для финда Кнайта. У моего алгоритма похожесть 90% (на 91% не найдёт картинку), а у Кнайта - это 85%. Неплохой вопрос почему?

Это если у Кнайта делать погрешность 0, а вот если делаю погрешность 10%, то и у Кнайта тоже похожесть становится 90%.

Автор: DarkMaster 8.4.2021, 13:03

Цитата
Думаю весь секрет скорости зарыт здесь. Возможно осуществляется и какой-то анализ экрана (то на чём ищется картинка).

Это оптимизации и не более. При поиске бОльшего числа картинок, чем есть на экране - оптимизации сразу становятся неактуальными. Если есть изображение подподающее под заданные параметры - он его 100% найдет.

Цитата
У моего алгоритма похожесть 90% (на 91% не найдёт картинку), а у Кнайта - это 85%. Неплохой вопрос почему?

Что есть похожесть? Есть точность и девиэйшн

Автор: sutra 8.4.2021, 13:08

Ну в общем, по крайней мере лично для себя, я вопрос решил. Не буду я больше ничего изобретать. Буду пользоваться своим. Надо просто грамотно организовывать поиск и никаких проблем не возникает.

Цитата
Что есть похожесть?

Это то, что показывает результат поиска у Кнайта, если найдена одна картинка - % похожести.

Моя терминология: похожесть - accurace; погрешность - deviation.

Автор: sutra 8.4.2021, 13:22

Я во всём люблю ясность. Любые ... если ... возможно - это не для меня. Всё должно быть абсолютно точно. Искаться то - что должно искаться и никак иначе. Но это лично моё субъективное мнение. А вам други - удачи, творческих побед и здоровья.

Автор: DarkMaster 8.4.2021, 13:26

У кнайта отображается точность при которой алгоритм посчитал достаточным для совпадения. Т.е. если мы зададим точность, скажем, в 80%, то первый же пиксель который перейдет границу в 80% будет последним. Т.е. имея, скажем, картинку из 100 пикселей и точность в 80% ты никогда не увидишь, что она нашлась с точностью 81%.

Автор: Cockney 8.4.2021, 13:38

Цитата(DarkMaster @ 8.4.2021, 13:26) *

У кнайта отображается точность при которой алгоритм посчитал достаточным для совпадения. Т.е. если мы зададим точность, скажем, в 80%, то первый же пиксель который перейдет границу в 80% будет последним. Т.е. имея, скажем, картинку из 100 пикселей и точность в 80% ты никогда не увидишь, что она нашлась с точностью 81%.



А в чем смысл ? А если требуется найти лучшее совпадение ? Мне всегда казалось это нижний порог, ниже которого точность не может быть.

Автор: cirus 8.4.2021, 13:57

Цитата
У кнайта отображается точность при которой алгоритм посчитал достаточным для совпадения. Т.е. если мы зададим точность, скажем, в 80%, то первый же пиксель который перейдет границу в 80% будет последним. Т.е. имея, скажем, картинку из 100 пикселей и точность в 80% ты никогда не увидишь, что она нашлась с точностью 81%.

Нет. Напишет процент совпадения от 80 до 100.

Автор: DarkMaster 8.4.2021, 14:05

Цитата
А в чем смысл ? А если требуется найти лучшее совпадение ? Мне всегда казалось это нижний порог, ниже которого точность не может быть.

Ну я положим тоже не знаю. Смысл теоретически мог быть заключен в скорости поиска, но для меня это тоже не совсем ясно. Приходилось постепенно повышать порог, чтобы понять когда реально находит. Этот момент тоже планировалось пересмотреть.

Под финды вообще в планах было сделать 2 функции под каждый. Одна человеческая, с нормальными параметрами, парсингом и т.д. и вторую - пушку в которой нет парсинга, левых преобразований, вызовов других функций и т.д. Это я к чему. В человеческую вполне укладывается лишний if даже если он реально нагрузит. Во вторую - естественно нет. А у нас на данный момент нечто среднее. Про финд все мысли... Пока привожу остальное в порядок. Написаны loadimage, image_copy (куска или целиком), переработан saveimage, пришлось еще и отражение по вертикали написать ибо в норме bmp перевернутый верх ногами, но почему-то старые функции его переворачивали на родину. Короче образец логичности и стандартизации пока что. Ну и доков там писать тонну надо еще. Нормально задокументировать + ~1к строк в гору.

Цитата
Нет. Напишет процент совпадения от 80 до 100.

Он это сделает если, скажем, у тебя изображение из 9 пикселей. Тогда у тебя каждый пиксель будет давать 11.(1)%, соответственно 7 пикслей мало, сработает только на 8 пикселях, а это будет 88.(8)%. Ну и получишь ты соответственно 88 или 89 точности (не знаю куда оно округляет, скорее всего trunc).

Кстати кусок памяти созданный malloc тоже вполне успешно грохается когда не надо, если он был local. Причем жесткая ирония в том, что грохается не смотря на присвоение upvalue массиву через вызов функции (в буфер добавляю).

Автор: cirus 8.4.2021, 14:17

Цитата
Он это сделает если, скажем, у тебя изображение из 9 пикселей.

30*30.
Цитата
Кстати кусок памяти созданный malloc тоже вполне успешно грохается когда не надо

Сборщик что ли удаляет?

Автор: DarkMaster 8.4.2021, 14:29

Цитата
Сборщик что ли удаляет?

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

Автор: sutra 8.4.2021, 18:14

Цитата
пришлось еще и отражение по вертикали написать ибо в норме bmp перевернутый верх ногами, но почему-то старые функции его переворачивали на родину

Вот и я про то же. Вопрос, а как его переворачивают? Поэлементно? Это же время, про которое мы тут толкуем. Я потестил получение битовой маски и с плюсом и с минусом, разницы не заметил. Разброс результатов большой (я уже говорил, что в lua тестить дело неблагодарное), но без какой-либо очевидной разницы.

Не, ну конечно loadimage не критичен наверное ко времени. Но я в очередной раз удивляюсь, как люди умудряются всё делать через жопу, в прямом смысле слова. Может конечно есть какая-то фишка в этом перевёртывании, но что-то никак не возьму в голову какая??

Вот сижу и думаю, как лучше переворачивать. Или может массово покарать грешников, и насильно обратить в правоверных? Наверное проще все файлы массово переделать под минус, делов то подправить в каждом заголовок, да переписать всё наоборот.

Автор: cirus 9.4.2021, 1:42

Цитата
Вопрос, а как его переворачивают?

Искать можно и перевернутую картинку, разумеется изображение, на котором искать тоже должно быть перевёрнуто.
Можно создать битмап в памяти и скопировать в него картинку, получить массив бит.
Либо копировать построчно через memcpy, обычно искомая картинка небольшой высоты, так что по времени это вообще ничего не стоит.

Автор: DarkMaster 9.4.2021, 3:11

Цитата
Вот и я про то же. Вопрос, а как его переворачивают? Поэлементно?

Я переворачивал построчно через memcpy.
Цитата
Не, ну конечно loadimage не критичен наверное ко времени. Но я в очередной раз удивляюсь, как люди умудряются всё делать через жопу, в прямом смысле слова. Может конечно есть какая-то фишка в этом перевёртывании, но что-то никак не возьму в голову какая??

Для меня вообще интересно за каким чертом изначально сделали через жопу сам формат битовой маски, который подразумевает именно перевернутый вид. Я так преполагаю кнайта задрало выносить мозг перевернутой алгоритмизацией и он просто крутанул. То же получение каринки через gdi по скорости не отличается, разница в написании заключается в минусах проставленных в двух местах. Так же есть версия, что метод 2 получает изображение не перевернутым и я практически уверен, что метод 1 который изначально был сделан работает именно в нормальном виде ибо это чисты get_pixel, который всю область собирает. Т.е. получился некоторый "исторически сложившийся" код, который вроде логичен и адекватен, но не соответствует стандартам(имхо не логичным и неадекватным). В итоге непонятный зоопарк.
Цитата
Вот сижу и думаю, как лучше переворачивать. Или может массово покарать грешников, и насильно обратить в правоверных? Наверное проще все файлы массово переделать под минус, делов то подправить в каждом заголовок, да переписать всё наоборот.

Хз. Имхо надо допилить финдимидж и обрабатывать все в перевернутом виде и ничего не крутить. Главный вопрос в том, как это делать... Впринципе можно и в старый финд подсовывать перевернутые изображения, но там тогда изменится порядок нахождения.
Цитата
Искать можно и перевернутую картинку, разумеется изображение, на котором искать тоже должно быть перевёрнуто.

Если брать луа, то искать перевернутую сохраняя направление поиска не очень удобно. for тут работать на понижение не умеет, т.е. работа пойдет через while, а он медленный. Ну и писать несколько тяжелее.

Автор: sutra 9.4.2021, 10:44

Цитата
Я переворачивал построчно через memcpy.

Спасибо. Да, именно так. Благо меня этому уже научили. Просто при загрузке файла смотрю высоту и если минус, построчно копирую снизу вверх. Тут тоже вроде всё решил, как и говорил, создал уже массив этих масок с большим запасом, так что манипулировать ими нет проблем. При записи не знаю пока, можно тоже построчно копировать в обратном порядке, чтобы не быть белой вороной. Скорость копирования в памяти - это крохи, тут нет никаких проблем, кроме эстетической - противно смотреть на собственный код.

Автор: sutra 9.4.2021, 11:20

Кстати, вырезалку из образа, тоже таким же способом.

Автор: Madeus 9.4.2021, 11:58

Цитата(DarkMaster @ 9.4.2021, 3:11) *

Если брать луа, то искать перевернутую сохраняя направление поиска не очень удобно. for тут работать на понижение не умеет, т.е. работа пойдет через while, а он медленный. Ну и писать несколько тяжелее.

For же умеет назад считать, с отрицательным шагом.

Автор: DarkMaster 9.4.2021, 12:06

sutra, принципиально делаем две версии одинаковые?)

Цитата
For же умеет назад считать, с отрицательным шагом.

хм. Видимо что-то с математикой косячил. Тупо не входило в цикл когда старт больше окончания и отрицательный шаг. Видимо что-то из этого было с ошибкой.

Автор: sutra 9.4.2021, 12:58

Цитата
sutra, принципиально делаем две версии одинаковые?)

Нет, Дарк, не в этом дело. Просто я люблю всё попробовать сделать сам, чтобы чётко понимать что и как работает, так сказать для опыта. Мои потуги нисколько не умаляют твой вклад в общее дело. Ну и в конце концов, ты же всё это делаешь не только для меня, хоть я тут намутил больше всех. Во-вторых, ну бывает просто не терпится поскорее решить проблему, а не ждать милостыню, да и кто знает, а вдруг тебе будет некогда ... Ну и наконец, я максималист, если у меня есть ощущение, что можно что-то ускорить - я это делаю. Я вообще любитель низкого уровня, мне как-то это ближе, ну кроме ASM-а конечно, хотя были времена, когда и без него ну никак было не обойтись. Когда рисовал обработку графики (ещё для CGA), там прирост по сравнению с Паскалем был очень солидный. Я же не зря спросил про копирование - это же основа работы с массивами данных. Теперь я знаю как это тут делать, так что всё в полном ажуре. Ещё раз спасибо и тебе и всем кто не был безучастен!

Автор: sutra 9.4.2021, 13:17

Дело всё ещё в том, что я совершенно не понимаю принципы работы lua. Что и чего там делается я не знаю. Изучать всё это - мне жаль убивать своё время, я далеко не молод. Тупо ориентируюсь на готовые решения (примеры) и на интуицию. Вот пример. rmem - это что? Я не знаю. Это аббревиатура доступа к данным? или полноценная функция? Если функция (о чём я интуитивно подозреваю), тогда это однозначные тормоза, т.к. уже задействуется память, т.к. используется стек, хотя х.з. как сейчас всё реализуется, может всё уже распёхано по памяти.
Раньше я чётко понимал ... передал в функцию параметр как var - передал адрес, без него - будет создана новая переменная, почему и было глупо передавать массивы как параметр, а не как переменную. Почему и используется буфер (даже понятие придумано конкретное) при передаче массивов данных ... Ну в общем ты меня понял, примерно так.

Автор: DarkMaster 9.4.2021, 13:24

Цитата
rmem - это что? Я не знаю. Это аббревиатура доступа к данным? или полноценная функция?

Это сишный cast.
Цитата
ну кроме ASM-а конечно

В луа можно делать вставки на аме crazy.gif
Не знаешь луа, но знаешь асм?) Пиши на асме - уже в продаже доступно.

Автор: cirus 9.4.2021, 13:26

Цитата
rmem - это что?

Тоже самое что ffi.cast.
Код
local rmem = ffi.cast

Для примера:
Код
--lua
local print = log
print(123)



Автор: sutra 9.4.2021, 13:42

Цитата
Это сишный cast.
biggrin.gif
И даже это требует для меня перевода. Ты думаешь я что-нить понял? СИ я тоже не изучал - дурак был, да и не было возможности, пришлось принципиально менять направление деятельности, я начал свой трудовой путь в августе 91 года, надеюсь не надо объяснять, что тогда творилось в стране. Пришлось всё бросить и надеть погоны. Да и ASM я напрочь забыл, да и знал то немного.

Конкретный вопрос. rmem тормозит или нет? Мне показалось что тормозит. Именно поэтому я и делал функцию ImageToArray.

Сейчас есть прямой доступ к битовой маске, вот поэтому и спрашиваю, есть ли смысл использовать rmem.

А вообще не тратьте на меня своё время, для меня всё это теперь просто хобби, забава. Мой поезд давно на запасном пути. Не сдохнуть бы и то хорошо. biggrin.gif

Автор: sutra 9.4.2021, 15:45

Дарк, давай уж приходи к единообразию. Если уж начал облагораживать меняя
math.floor(w*3/4+0.75)*4 на math.ceil(w*3/4)*4
Так везде поменяй. Или ты хотел продемонстрировать ВСЁ многообразие подходов и методов решения.

Автор: DarkMaster 9.4.2021, 15:49

Цитата
И даже это требует для меня перевода.

Это функция си, которая преобразует один тип данных в другой. Зачастую используется чтобы компилятор отстал со своими претензиями, что ты подсовываешь какой-то сраный int вместо MicrosoftSuperReanamed5or6TimesDataTypeForLookSoCool, который на самом деле все тот же int. Если совсем по деревянному:
a=int
b=a
c=b
d=c
-- а вот тут тебя копилятор пошел нахрен ибо
-- функция ждет на входе тип данных d.
-- и пофиг, что оно все друг другу равно.
result = cool_func(int)
-- а если ты сделаешь
result = cool_func(cast"d" int)
-- то оно заткнется и сожрет.
-- и вот за этот бред мне очень хочется убивать.
-- причем зачастую нативно не понятно, что ему надо
-- на входе. int/unsigned int/word/long long или еще чего.

Цитата
Конкретный вопрос. rmem тормозит или нет?

Да, тормозит. Насколько? А кто бы знал. Я очень сильно подозреваю, что это его вина в проблемах финдимиджа.


По поводу malloc и приколов garbage collector, который хвостиком махнул и снес все к хренам. Если произошло преобразование cdata pointer -> number -> cdata pointer, то для него уже связь потеряна, можно сносить. В остальном шуршит.

Автор: sutra 9.4.2021, 16:12

Цитата
Это функция си

Ну вот видишь, "предчуствия меня не обманули". biggrin.gif

Цитата
функция ждет на входе тип данных d

Вот именно это мне и не нравится. Я понимаю, хотели сделать всё для дурачка, а в итоге, как обычно мучаются специалисты. Меня лично просто бесит, когда вместо меня начинают думать. Если я сказал byte - значит байт, и не надо за меня решать. Извини, привычка думать по Паскалевски.

Примерно я всё понял, нужен тотальный контроль типов данных.

А ещё меня раздражает, что всё так сильно перемешано по типам. Лично для меня существуют лишь следующие типы: 1 байт; 2; 4; 8; 16; 32 и 64. А как я их буду интерпретировать, не их собачье дело.

В идеале доступ к нетипизированным данным, что собственно мы и делаем, объявляя массивчик для битовой маски. Есть доступ к любому байту, а уж что-то умножить или разделить на 256 мы всегда сумеем сами. Кстати, мне очень помогают твои скрипты, я на них учусь, я только на днях узнал (изучая твои поделки), что можно просто использовать %, чтобы получить целое от деления, вместо бесящих меня math.

Автор: DarkMaster 9.4.2021, 16:17

Цитата

А ещё меня раздражает, что всё так сильно перемешано по типам. Лично для меня существуют лишь следующие типы: 1 байт; 2; 4; 8; 16; 32 и 64. А как я их буду интерпретировать, не их собачье дело.

Ну целые со знаком/целые без знака/с плавающей точкой имхо адекватное решение. Ну тупо чтобы +-*/ и прочая математика не дурела и каждый раз не писать велосипед.
У меня когда был подход лет 10 назад к си, так у меня самыми используемыми были memcpy, там где надо и не надо и pragma pack, чтобы он выравнивать перестал и можно было спокойно с памятью работать. У сишников была кровь из глаз(и я согласен с этим) и вопросы "а оно реально работает?"))

Цитата
что можно просто использовать %, чтобы получить целое от деления, вместо бесящих меня math.

Но не нужно. Тормозной он. Я всегда стараюсь избегать, но тут мне было так лень, да и там всего одна операция.

Деление кстати еще тормозное. Лучше заменять умножением, если это возможно.

Автор: sutra 9.4.2021, 16:38

Цитата
Ну целые со знаком/целые без знака/с плавающей точкой имхо адекватное решение

Ну конечно, это уж я так. Хотя мне ничего не мешает в нужный момент интерпретировать так, как я хочу. Ну вот есть у меня минусовое. Когда я это пишу в файл - глубоко фиолетово что там имелось ввиду - запишется конкретная комбинация из 8 битов.

Грубо говоря всегда должна быть возможность работы с буфером. Понятно, что она есть, но в lua это всё так хитро накручено, что мне с моими 3 классами приходится туго.

Вот хоть верь, хоть не верь. До сих пор работаю даже на битовом уровне. Как уже говорил, у меня всё работает по сценариям, которые прописаны в обычных текстовых файлах. Мне нужно задать кучу параметров по принципу ДА-НЕТ. Тащить массив очень неудобно, а кинуть число, в котором зашиты все эти да и нет очень удобно.

Точнее, текстовый файл - это и есть массив, в котором каждое число тоже массив, только битовый - очень удобно для читабельности таких файлов.

Цитата
Но не нужно. Тормозной он

Странно, вопрос, а чем же так тогда принципиально быстр math.modf ?

Автор: DarkMaster 9.4.2021, 16:55

Цитата
Странно, вопрос, а чем же так тогда принципиально быстр math.modf ?

А беспонятия. Где-то на бенчмарк натыкался.

Автор: sutra 9.4.2021, 17:06

Да брось, это всё просто крохи, для смеха попробовал.
for i=1,100000000 do
a=8%3
-- a=math.modf(8/3)
end

За то как красиво и элегантно, и запоминаемо, я до сих пор путаю modf и fmod

Автор: Cockney 9.4.2021, 17:30

Цитата(DarkMaster @ 9.4.2021, 15:49) *

Это функция си, которая преобразует один тип данных в другой. Зачастую используется чтобы компилятор отстал со своими претензиями, что ты подсовываешь какой-то сраный int вместо MicrosoftSuperReanamed5or6TimesDataTypeForLookSoCool, который на самом деле все тот же int. Если совсем по деревянному:
a=int
b=a
c=b
d=c
-- а вот тут тебя копилятор пошел нахрен ибо
-- функция ждет на входе тип данных d.
-- и пофиг, что оно все друг другу равно.
result = cool_func(int)
-- а если ты сделаешь
result = cool_func(cast"d" int)
-- то оно заткнется и сожрет.
-- и вот за этот бред мне очень хочется убивать.
-- причем зачастую нативно не понятно, что ему надо
-- на входе. int/unsigned int/word/long long или еще чего.



Это не бред. Это очень хорошие пинки компилятора(а он умнее, можно не спорить, это факт) глупому человеку. Каст явно режет точность числа, например double a = 2.0 не равно int b = 2 по определению представления дробных чисел. И передав 2.0 в функцию, которой нужен int. Вы уверены что хотите потерять дробную часть ? Компилятор и говорит об этом. И так со знаковыми типами. Компилятор все проверяет. И это хорошо. Если это вызывает ужас, то я думаю после знакомства с rust можно получить инфаркт.

Цитата(sutra @ 9.4.2021, 16:12) *

А ещё меня раздражает, что всё так сильно перемешано по типам. Лично для меня существуют лишь следующие типы: 1 байт; 2; 4; 8; 16; 32 и 64. А как я их буду интерпретировать, не их собачье дело.



Эта идеология умерла минимум 30 лет назад. И это хорошо.

Автор: DarkMaster 9.4.2021, 17:47

Добавлен loadimage, image_mirror_v(по вретикали переворачивает), image_copy.
Опять переработан буфер на 10 раз. Переделан saveimage, getimage и все, что только можно.
loadimage сделан по хардкору по оффсетам. Вроде как работать должно на всех версиях и офсеты никогда не менялись. Будет смысл хоть какой-то - можно переписать с получением всех заголовков (зачем мне пока не ясно).

Удаление изображений по image = nil пока не реализовано, только deleteimage.


Прикрепленные файлы
Прикрепленный файл  color.lua ( 68,94 килобайт ) Кол-во скачиваний: 57

Автор: DarkMaster 9.4.2021, 17:59

Цитата
Это не бред. Это очень хорошие пинки компилятора(а он умнее, можно не спорить, это факт) глупому человеку. Каст явно режет точность числа, например double a = 2.0 не равно int b = 2 по определению представления дробных чисел. И передав 2.0 в функцию, которой нужен int. Вы уверены что хотите потерять дробную часть ? Компилятор и говорит об этом. И так со знаковыми типами. Компилятор все проверяет

Я понимаю, когда я вместо int сую double. Речь о том, что когда ожидаются 4 байта целого и ты ему посылашь 4 байта целого, а он орет, что тип не тот, хотя там тупо через define все это переобъявлено. Если чесловек вместо int сует double, то он скорее всего идиот, уж простите =)

Автор: Cockney 9.4.2021, 20:34

Цитата(DarkMaster @ 9.4.2021, 17:59) *

Я понимаю, когда я вместо int сую double. Речь о том, что когда ожидаются 4 байта целого и ты ему посылашь 4 байта целого, а он орет, что тип не тот, хотя там тупо через define все это переобъявлено. Если чесловек вместо int сует double, то он скорее всего идиот, уж простите =)



Ну с другой стороны, ошибок на пустом месте тоже не бывает, так ? Значит все же не совсем те же 4 байта целого. Возможно тип знаковый. Я вот сейчас поигрался с виндовым LPCSTR и некоторыми типами из библиотеки c++ - не вижу проблем. Можно проблему как-то воспроизвести ? Кусочек кода там показать.

Автор: Cockney 9.4.2021, 20:47

Цитата(DarkMaster @ 9.4.2021, 17:59) *

Если чесловек вместо int сует double, то он скорее всего идиот, уж простите =)



Ну почему, вот ситуация - имеем хеш мапу. Получили хеш. Получаем индекс корзины, т.е. просто деление хеша на кол-во корзин. Результат дробный. Дальше по этому индексу (а он обрежется до целого) вставляем элемент. Вопрос в том предупредит ли компилятор об этом или нет. Таких мест может быть миллион на 100 строк кода.

Автор: cirus 10.4.2021, 2:41

Код
for i=1,100000000 do
a=8%3
-- a=math.modf(8/3)
end

Такие тесты вообще ничего не отражают. То что выполняется быстрее в одном цикле может выполняться дольше в скрипте где несколько циклов, много переменных и т. д.

Автор: DarkMaster 10.4.2021, 3:26

Цитата
Ну с другой стороны, ошибок на пустом месте тоже не бывает, так ?

Бывают и очень много. Ошибок от непонимания, как "оно" должно работать лично у меня очень мало. Невнимательность, звонки, перерывы - топ причин.
Цитата
Я вот сейчас поигрался с виндовым LPCSTR и некоторыми типами из библиотеки c++ - не вижу проблем. Можно проблему как-то воспроизвести ? Кусочек кода там показать.

Вообще просьба вытащить код 10 летней давности, который был освоением либо "надо сделать, за нами москва" это конечно идея интересная, но тут нужен стеклянный шар и духи, на край некромант. Не сыскать этого уже давно, у меня даже студии уже нет много лет.
Код
    BOOL WriteFile(
        HANDLE       hFile,
        LPCVOID      lpBuffer,
        DWORD        nNumberOfBytesToWrite,
        LPDWORD      lpNumberOfBytesWritten,
        void*        lpOverlapped
    );

Вызови без кастов передав void*, void*, unsigned long, unsigned long*. А еще лучше вместо unsigned long* передать тот же void* - указатель это указатель, ничего нового в нем не будет. А можно и довести и вовсе до предела - передать вместо void* любимый и красивый unsigned int. Матом крыть не будет?

Цитата

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

На мой взгляд приведение разных типов данных должно быть явным, если возможна потеря данных. Касатаельно плавающей точки, так и вовсе возможно 3 приведения: отброс мантиссы, округление вверх, округление до целого. И лично мое мнение, если подобное преобразование используется, то оно должно быть явным.
Преобразование же из 4 байтов беззнакового целого в 4 байта беззнакового целого (обозванного иначе) - для меня это бред сивой кобылы. При всем моем очень глубоком уважении к си (без иронии). Понятно, что может быть преобразование little/big endian, можно вообще выдумать другой формат хранения целого в этих 4 байтах, но речь то не об этом, речь о полностью идентичном формате, переобъявленном 10 раз.

Автор: cirus 10.4.2021, 9:20

Цитата
Матом крыть не будет?

Нет, если объявишь void, то будет принимать всё подряд.
Цитата
LPCVOID lpBuffer,

Код
void*  lpBuffer



Автор: DarkMaster 10.4.2021, 9:55

Цитата
Нет, если объявишь void, то будет принимать всё подряд.

Т.е. это предложение объявлять обратно переобъявленное?) Сильно.
Ну ладно мы тут через ffi фактически заново сами все это объявляем, но если взять си/плюсы. Там тоже переобъявлять обратно предлагаешь?

Автор: sutra 10.4.2021, 10:56

Цитата
Эта идеология умерла минимум 30 лет назад.

Ничего она не умерла. И не умрёт, во всяком случае пока мы будем использовать двоичную систему, кстати очень убогую с точки зрения логики. Самый простой пример, наша битовая маска, которая 24 бита. Да, для нас это 24 бита, а в реальности это всё порции по 4 байта и как хотите их обзывайте, только они так и останутся 4 байтами. Дело то не в компиляторе, которые пишут люди, а в людской гордыне. Каждый придумывает свою хрень, вместо того, чтобы прийти хотя бы к элементарным стандартам. И вместо того, чтобы решать реальные задачи, приходится заниматься вот такими глупостями, как изучением очередного дерьма, которое, используя вашу терминологию, тоже однозначно умрёт, думаю намного раньше, чем через 30 лет.

Цитата
Такие тесты вообще ничего не отражают

Я же абсолютно ясно сказал - для смеха biggrin.gif Возможно что-то где-то и тормозит, вопрос а на сколько? на 1 миллионную долю секунды? Тут такие якоря люди делают, такие вещи, совершенно не думая ни о какой скорости, что этот примитивный оператор - это просто невинное дитя.

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

Моя общая мысль очень проста, я не собираюсь изобретать колесо. Напротив, хотелось бы применять это колесо, создавая например автомобиль, а не неся это колесо на своей спине. Ну вот ведь очень забавно, ты Cirus взял и откопал способ получения битовой маски. А что 2-3 года назад этой возможности не было?? Была, просто мы о ней не знали. А про звук? Да наверняка такая же элементарщина, просто мы не знаем как к ней получить доступ. Вот про что я говорю, а не про синтетику тестов.

Автор: Cockney 10.4.2021, 11:39

Цитата(DarkMaster @ 10.4.2021, 3:26) *

Бывают и очень много. Ошибок от непонимания, как "оно" должно работать лично у меня очень мало. Невнимательность, звонки, перерывы - топ причин.

Вообще просьба вытащить код 10 летней давности, который был освоением либо "надо сделать, за нами москва" это конечно идея интересная, но тут нужен стеклянный шар и духи, на край некромант. Не сыскать этого уже давно, у меня даже студии уже нет много лет.
Код
    BOOL WriteFile(
        HANDLE       hFile,
        LPCVOID      lpBuffer,
        DWORD        nNumberOfBytesToWrite,
        LPDWORD      lpNumberOfBytesWritten,
        void*        lpOverlapped
    );

Вызови без кастов передав void*, void*, unsigned long, unsigned long*. А еще лучше вместо unsigned long* передать тот же void* - указатель это указатель, ничего нового в нем не будет. А можно и довести и вовсе до предела - передать вместо void* любимый и красивый unsigned int. Матом крыть не будет?



VC++ 2017 CE, C++17
Код

const unsigned long l = 0;
WriteFile(l, l, l, l, l);


Ни одного предупреждения даже на самом строгом уровне компиляции. Единственное, что не вышло это отправить unsigned int в lpBuffer, причина - LPCVOID объявлен как const char*, т.е. константный указатель, и передача без явного каста запрещена. Я же просто добавил const к переменной l. Из этого становится очевидно, что компилятор кроме размерности данных отслеживает еще и другие свойства их.


Цитата(sutra @ 10.4.2021, 10:56) *

Ничего она не умерла. И не умрёт, во всяком случае пока мы будем использовать двоичную систему, кстати очень убогую с точки зрения логики. Самый простой пример, наша битовая маска, которая 24 бита. Да, для нас это 24 бита, а в реальности это всё порции по 4 байта и как хотите их обзывайте, только они так и останутся 4 байтами. Дело то не в компиляторе, которые пишут люди, а в людской гордыне. Каждый придумывает свою хрень, вместо того, чтобы прийти хотя бы к элементарным стандартам. И вместо того, чтобы решать реальные задачи, приходится заниматься вот такими глупостями, как изучением очередного дерьма, которое, используя вашу терминологию, тоже однозначно умрёт, думаю намного раньше, чем через 30 лет.



Стандарты есть. Уже давно. И умирать они не планируют, напротив, все больше ужесточаются в угоду нормального кода. И благодаря им решаются действительно реальные задачи, а не поиск по битовым маскам. Да и причем тут двоичная система ? Никто на нее не посягает. Вопрос в том, что уже нет нужды биты выдирать руками, с этим справляется лучше машина и зачастую лучше чем человек.

Автор: sutra 10.4.2021, 11:47

Не о том господа думаем. Про скорость. Мне вот совершенно не нравится такая вещь: j=j+1. В Паскале была такая незаменимая для счётчиков вещь как инкремент (inc и dec). Там отсутствует контроль выхода за допустимый диапазон типа данных, а он в нашем случае как раз и не нужен. Наверняка ведь можно прикрутить такую штуку и в нашем финде.

Что-то мы не можем заставить машину картинки искать. Я ни на кого не посягаю, пусть делает каждый что хочет, а я буду делать то что я хочу.

Цитата
реальные задачи, а не поиск по битовым маскам

Это понятно, что Илону Маску наплевать на наши битовые маски. Но вот только для нас это как раз и есть реальная задача. С которой мы как-то очень плохо справлялись до недавнего времени.

Автор: DarkMaster 10.4.2021, 11:56

Цитата
Ни одного предупреждения даже на самом строгом уровне компиляции.

Блин аж руки чешутся старую студию налить. Было больше 10 лет назад, как раз на границе перехода на 2011 стандарт, может уже и что-то путаю, но вообще был уверен прям ну совсем. А может прогресс все-таки дошел. Тем не менее переобъявления сами по себе от этого имхо умнее стали, хотя проблем безусловно меньше. А handle вместо, скажем dword сожрет?)
А на функции работы со строками которые нормальные сишные, на не _s материться перестал?)

Автор: Cockney 10.4.2021, 12:07

Цитата(DarkMaster @ 10.4.2021, 11:56) *

Блин аж руки чешутся старую студию налить. Было больше 10 лет назад, как раз на границе перехода на 2011 стандарт, может уже и что-то путаю, но вообще был уверен прям ну совсем. А может прогресс все-таки дошел. Тем не менее переобъявления сами по себе от этого имхо умнее стали, хотя проблем безусловно меньше. А handle вместо, скажем dword сожрет?)
А на функции работы со строками которые нормальные сишные, на не _s материться перестал?)




Не сожрал)

А в чем ругань заключалась ?

Автор: sutra 10.4.2021, 12:09

И у меня есть ощущение, что if-ы слишком медленны. 250 лямов выполняются 2 секунды. Наверное как раз на битовом уровне это и можно было бы ускорить. Собственно что надо, узнать попадает ли конкретное число в заданный диапазон (всего в пределах 1 байта). Число целое - всё должно просто летать. А этот lua кто знает чего он там сравнивает, какие типы там у него.

Автор: DarkMaster 10.4.2021, 12:17

Цитата
А в чем ругань заключалась ?

Да я бы не сказал, что ругань. Скорее наоборот, я поддержал sutra в некоторых старперских взглядах, которые мне близки =)

Автор: sutra 10.4.2021, 12:33

Вот ещё интересная вещь. Заменил if j==sim на if rawequal(j,sim) и получил тормоз более 15%. Было 1.98 сек. а стало 2.34. Хотя по логике вроде бы должно быть наоборот.

Не понимаю я lua, от слова совсем. Надо попробовать полностью всё перевести на СИ. Просто интересно посмотреть на результат.

Автор: sutra 10.4.2021, 14:19

Короче, компилятор тут мама не горюй, как он чего делает, совершенно не понятно.
Простенький тестик, для смеха.

for i=1,200000000 do
if 8%3 ==2 then a=a+1 end -- 187 - 219 тысячных сек.
-- if 8%3 ~=2 then break end -- 60 - 70 тысячных сек.
end

ВОПРОС. А чего он у меня целых 2 секунды делает, если там ещё всё проще. Обычное сравнение. Сравнил - пошёл дальше. Вот вам и тормоза на абсолютно ровном месте. Или это так вайлы тормозят.
ОТМЕЧУ! Тестик на так называемом тормозе под названием процент.

Короче, однозначно надо прикручивать инкремент, наверняка это можно сделать. И очень внимательно пересмотреть всю математику циклов.

Или он так долго шарится по памяти. Ведь на порядок медленнее в финде.

Автор: sutra 10.4.2021, 14:37

Тестанул цикл в цикле - результаты не изменились

for k=1,100 do
for j=1,100 do
for i=1,20000 do

Возникают вопросы, на которые не могу дать ответа.

Автор: DarkMaster 10.4.2021, 15:27

Я в понедельник или во вторник опять на финдимидж насяду. Надо победить. Хочу попробовать перевести в массив, посмотреть результат, попробовать в 3 мерный сделать, думаю все-таки для теста попробовать на сях скомпилить - там по крайней мере точно будет понятен предел скорости. Это даст ответы имеет ли смысл дальше мучить луа и материться на него (думаю, что да) либо успокоится и искать решения именно в алгоритмизации.

Автор: sutra 10.4.2021, 15:50

Ты читаешь мои мысли. Я тоже хотел попробовать всё это сделать на Дельфине. Чтобы точно понимать все причины. Если честно, мне просто лень, поскольку меня то лично всё устраивает, я ничего не ищу при 80%. Но что-то мне подсказывает, что дело то именно в математике. Хочется всё-таки окончательно разобраться в этом вопросе, чтобы в будущем не наступать на те же грабли. Плюс там ещё вдобавок есть директивы компиляции, то же бы невредно посмотреть разницу. Не знаю, смогу ли себя заставить сесть за это дело.

Автор: DarkMaster 10.4.2021, 15:57

Ну я себя в любом случае заставлю. Надо ставить точку и хотя бы в бета-релиз все выводить.

Автор: sutra 10.4.2021, 15:58

Я почему и начал грешить на математику, поскольку логика Кнайта мне абсолютно понятна. Он же сам об этом говорит, он начинает с самого уникального "цвета" (наиболее редкого) и так в порядке уменьшения уникальности, что и я всегда пытался делать. Только он это делает путём анализа картинки, а я сам - ручками. Увеличивая deviation, естественно уменьшается уникальность пикселей, увеличивается количество сравнений. В принципе то всё понятно. Так что обязательно надо посмотреть что там с математикой.

Автор: DarkMaster 10.4.2021, 16:50

Цитата
начинает с самого уникального "цвета" (наиболее редкого) и так в порядке уменьшения уникальности

Это работает исключительно для поиска лимитированного количества, особенно хорошо работает для одиночной картинки. На безлимите сразу все сыпится по оптимизациям, на самом деле они начинают тянуть вниз. Другое дело, что обычно ищется как раз так только одна картинка.

Автор: Madeus 10.4.2021, 17:29

Цитата(DarkMaster @ 10.4.2021, 16:50) *

Это работает исключительно для поиска лимитированного количества, особенно хорошо работает для одиночной картинки. На безлимите сразу все сыпится по оптимизациям, на самом деле они начинают тянуть вниз. Другое дело, что обычно ищется как раз так только одна картинка.

2 поиска) Если одна картинка ищем быстро, если больше то извините ожидайте мы ищем (с) Почта России

Автор: sutra 10.4.2021, 17:51

Цитата
2 поиска) Если одна картинка ищем быстро, если больше то извините ожидайте мы ищем

Да на самом деле если не использовать процентовку схожести картинки, то ищется всего 1 сотую секунды. Я по этому и не парюсь. По факту всё изумительно ищется, тут просто вопрос принципа, откуда потеря скорости.

Автор: DarkMaster 10.4.2021, 18:09

Процентовка нужна. Надо мне искать сотню изображений - я не буду сидеть и рисовать их неделю, мне сделать надо) Если так каждый раз подбирать, то это можно бросать все дела в жизни и только сидеть и рисовать их, резать. Помню был проект, так там было под 1к изображений. Это же сколько надо их обрабатывать потом?

Автор: sutra 10.4.2021, 18:30

Да почему рисовать, почему резать? Всё ищется без процентовки, у меня это параметр sim (similar - похожесть) . Лично я её использую только при поиске перекрытых друг другом картинок (мишеней например и там нет таких размеров, всё быстро поэтому) ну и иногда возможен вариант, когда тоже без similar не получается, если может быть расположен на картинке прицел (и тоже нет пропуска такого гигантского количества пикселей). Всё остальное ищется диапазоном, который я задаю 2-мя параметрами dev - абсолютное отклонение (требуется обычно для тёмных пикселей) и acc - процент отклонения цвета. Мой алгоритм как раз и позволяет автоматом задавать поиск. Надо мне искать только "красненькое", не обращая внимание на другие пиксели картинки. Я и формирую псевдокартинку условием r=100, R=255 или как в твоём примере задал фон r=195 и всех делов.
На самом деле всё чудесненько и быстренько ищется. Просто если и similar ускорить - это будет КОСМОС.

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

Автор: sutra 10.4.2021, 18:40

И наконец. Предположим, найдём и устраним тормоза. Что получим? Полсекунды поиска на твою картинку - это всё равно очень много. Ведь наверняка надо искать не одну только картинку. Так что запросто возможен вариант, когда для особо нагруженных случаев, просто придётся "рисовать" и "резать". Не сделаешь ты так, что ну прямо ищется абсолютно всё, да ещё и за десятую секунды.

Автор: DarkMaster 10.4.2021, 18:44

Цитата
Лично я не знаю ни одного случая и даже не могу представить мысленно, когда меня бы что-то напрягло так, что это не ищется

Частично прозрачный текст на изменяющемся фоне с нечеткими границами, отрисованный через сглаживание шрифтов. У меня было... Печальная история.

Цитата
Полсекунды поиска на твою картинку - это всё равно очень много.

Стандартный финд - 7 соток. Я бы может и не трогал даже, но меня совсем не устраивает то, как там задается deviaton.

Автор: sutra 10.4.2021, 18:49

И ещё один довод, тоже уже говорил про это. Иногда наоборот эта процентовка только мешает искать то, что надо. Тоже надо думать и понимать какой процент схожести задавать. 80% - это очень много. Пятая часть предполагается мусор - это что же за картинка такая???

Цитата
Частично прозрачный текст на изменяющемся фоне с нечеткими границами, отрисованный через сглаживание шрифтов. У меня было... Печальная история.

Да не поможет тут процентовка, диапазоном надо играть. ПРОЦЕНТОМ отклонения цвета, а не пропуском мусора.

Я понял про что ты говоришь. Так буковки то МАЛЕНЬКИЕ, там даже пропуск мусора не влияет на скорость. Да - это наиболее сложный поиск. Без головы юзер его не выполнит, а с головой я тебе найду запросто. Возможно придётся создать несколько наборов картинок, я такими наборами влёгкую побеждаю сглаживание. Всё ищется, на практике уже, а не в теории.

Автор: DarkMaster 10.4.2021, 18:53

Цитата
Да не поможет тут процентовка, диапазоном надо играть. ПРОЦЕНТОМ отклонения цвета, а не пропуском мусора.

Угу, а потом оказывается, что один из самых контрастных и значимых пикселей прыгает в право-влево на единичку. А такие будут в каждом изображении при выше описанных условиях. И тут как раз точность очень сильно поможет. Обрезать весь фон тоже не реально. В итоге приходится искать не понятно что, и не дай бог еще спутать разные символы. Частично перекрытые изображения тоже бывают не так редко. Вообще из практики могу сказать, что если задаешь больше 95% точности, то это к прекрасным вечерам в пэинте. 92% самая золотая середина.

Автор: sutra 10.4.2021, 18:55

Я не возражаю 90% для буковок - ты даже не заметишь потерь скорости.

И, как правило, буковки ты ведь не ищешь по всему фулХД.

Автор: DarkMaster 10.4.2021, 18:56

Цитата
Я не возражаю 90% для буковок

Для текста, как правило, нужно больше, иначе путать начинает( Там где-то 97 при сложных условиях, меньше опасно..

Автор: sutra 10.4.2021, 18:58

Вопрос стоит конкретно - АЛГОРИТМ - найти причину, вот и всё. Это даст прирост, а он лишним не бывает.

Цитата
Для текста, как правило, нужно больше, иначе путать начинает( Там где-то 97 при сложных условиях, меньше опасно..

Да буковки ищутся (в смысле процесс поиска, а не результат )мгновенно при любых параметрах - они же МАЛЕНЬКИЕ!!!

Автор: DarkMaster 10.4.2021, 19:03

Цитата
МАЛЕНЬКИЕ!!!

Это пока у тебя их не тыща картинок) Сглаживание, jpg эффекты, фон, прозарчность.

Автор: sutra 10.4.2021, 19:17

Цитата
Это пока у тебя их не тыща картинок) Сглаживание, jpg эффекты, фон, прозарчность.

А ты пробовал? Я одним образом ищу примерно полторы тысячи, причём некоторые по нашим меркам огромные, типа 500*20, конечно я не лопачу весь экран, ищутся позиции, а по ним картинки.

До последнего времени самое долгое 27-28 тысячных - это был getimage.

Дарк, я же тебя не отговариваю, наоборот говорю, ищи причину, делай! biggrin.gif Ну а если не получится, ну тогда обращайтесь, 12$ в час, буду искать ваши картинки. biggrin.gif

Автор: sutra 10.4.2021, 22:56

И у тебя Дарк, всё-таки короткая память. Я ведь ещё в 19 году тебе говорил, в смысле спрашивал, а как ты игнорируя мусор (отсеивая какую-то часть пикселей) будешь отличать например "О" от "Q" или "1" от "l" . Пропуск мусора - это крайний случай, прибегать к которому надо абсолютно осознанно и понимая что должно искаться, а что нет. В большинстве случаев пропуск - это минус, так как это говорит лишь о том, что либо ищется не то что надо, либо что надо не ищется вовсе. Ну а в каких случаях требуется отсев уже устал повторять. У Кнайта изначально не было deviation и отсев нежелательных пикселей - это была просто необходимая данность. Потом всё было уже серьёзно переработано, но по умолчанию так остались 20% отсева (80% точности).

Автор: sutra 10.4.2021, 23:07

Главный минус у Кнайта, почему и используется так называемая точность, без которой частенько не ищется картинка - это то, что deviation - это процент отклонения. А какой процент отклонения задавать для пикселя с RGB (0,0,0)? У меня этот параметр - абсолютное значение (которое и нужно чтобы искались "тёмные" пиксели), процентовка идёт плюсом, и всё это в любых вариантах использования.

Автор: sutra 10.4.2021, 23:18

Я даже специальный скрипт делал, который мне показывал с какой гарантией ищется та или иная картинка, показывая допустимые граничные значения используемых мною параметров dev и acc в различных их сочетаниях. И я выбирал средние значения. Судя по моим экспериментам, запас варьирования всегда очень нехилый.

Автор: sutra 11.4.2021, 12:51

Дарк, наверное в скринилке, нужно всё-таки положительную высоту всегда прописывать - это типа норма. Наткнулся, что один старенький редактор не понимает файлы с отрицательной высотой. Короче, для себя принял именно общепринятый стандарт. Редактор - Paint Shop Pro 6 . Я им частенько пользуюсь. Установки не требует, простенький, не прихотливый.

Нет смысла изголяться. Перевёртывание происходит мгновенно, да и не только перевёртывание. Образ + скрин = молниеносно.

Автор: sutra 11.4.2021, 17:41

Дарк, у тебя там косячки в скринилке, забыл наверное, на автомате клепал - исправь. Они вроде ни на что не влияют, но чисто для порядка.

C.WriteFile(f, ffi.new("uint32_t[1]", h*w*3+54 )

Надо множить не на тройную ширину, а на длину строки, ну и далее тоже. Про то что там далее я не в теме (хотя все пишут по длине строки), но размер то файла точно врёт.

Автор: sutra 11.4.2021, 18:52

И мне как-то не понравилась запись в файл кусками. По древней, старпёрской привычке делать запись в файл одним оператором я сделал по-своему. Сначала сформировал единый заголовок, а потом допинываю требуемыми значениями.

Код

  ffi.C.memcpy(bmpHeader+2,ffi.new("uint32_t[1]",h*lD+54),4)
  ffi.C.memcpy(bmpHeader+18,ffi.new("uint32_t[1]",w),4)
  ffi.C.memcpy(bmpHeader+22,ffi.new("uint32_t[1]",h),4)
  ffi.C.memcpy(bmpHeader+34,ffi.new("uint32_t[1]",h*lD),4)

Соответственно запись заголовка в файл идёт за один заход.

Автор: sutra 12.4.2021, 13:44

Парни, спасибо вам за моё обучение! Как хорошо, что надоумили плюнуть на память!
Надоела мне эта возня с типами данных и адресами. Сделал один глобальный СИ-массив, под разные случаи жизни, видимый всеми и везде и никаких проблем. Например нулевой массив содержит 4-х байтовые величины, с индексами 1 и 2 - байтовые и т.д.. И теперь работа с выделением памяти под переменные просто отсутствует. И можно пихать что угодно, куда угодно, с минимальными потерями времени. Да, не эстетично, не правильно с точки зрения организации кода. Только на кой мне эта правильность, если lua (на мой вкус) сам трижды неправильный. Для примера, как переделал код и избавился от этих "режущих глаз" new:

Код

  CI[0][0]=h*lD+54  ffi.C.memcpy(HeaderBMP+2,CI[0],4)
  CI[0][0]=w        ffi.C.memcpy(HeaderBMP+18,CI[0],4)
  CI[0][0]=h        ffi.C.memcpy(HeaderBMP+22,CI[0],4)
  CI[0][0]=h*lD     ffi.C.memcpy(HeaderBMP+34,CI[0],4)
  for i=y2,y1,-1 do   -- цикл перевёртыш
      ffi.C.memcpy(CI[d]+iD,CI[s]+iS,w*3)  iD,iS=iD+lD,iS-lS
  end

Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)