Собственно обсуждение багов, фич, функционала, тесты и прочее.
Я в теме. Я давно отказался от передачи массивов. Делаю сразу глобальный массив (достаточно большой) через ffi , который использует и финдколор и все кому надо, и никаких тормозов. Если данные требуются позже (что крайне редко) ничто не мешает запомнить их отдельно. Понятно что это не эстетично, но совершенно нет потерь времени, необходимое при выделении памяти на массивы. По факту, при адекватных зонах поиска, время поиска - НУЛЬ.
Мне очень удобно. Грубо говоря вызываю if findcolor(... и данные поиска уже лежат в глобальном массиве и ими пользуешься напрямую в любых следующих действиях .
Сейчас буду пробовать твой вариант, если получится, то проблема будет решена для всех случаев жизни - спасибо!
Кстати, вот ещё замечание. От мощности видюхи зависит скорость доступа к данным, остальное от камня. Я успел до кризиса поменять на RTX 2070 - как чувствовал, что подорожают. Небольшой прирост в скорости всё-таки получил.
Не помню сколько у меня на фул хд. Я же сказал при "адекватных". Искать совсем не зная что и где - наверное тут и нет таких требований к скорости. Если искать мишени по всему экрану, то двух сотых вполне достаточно, чтобы потом всех их пристрелить, за полсекунды.
Всё ОК. Спасибо.
Да я понял, но у меня в основном нагрузка на файндимидж. Поиск конечно идёт по локальным позициям, а не по всему экрану. Ищутся все кнопки, все цифры, все наименования. Один getimage и потом мгновенно всё распознаётся. Ищу как и говорил ранее, не картинки а силуэты!! Например силуэты цифр состоят из 2-х ... 5-ти пикселей, которые и анализируются. Сначала создаются готовые наборы таких силуэтов, от 3-х, до 6-ти на каждый символ. И этого хватает для однозначного поиска. Главное ручками (и головой) правильно создать эти силуэты. Я делаю для себя. А ты ставишь суперзадачу. Сделать алгоритм, работающий за тупого юзера. Будет время потестю, поделюсь результатами.
Конечно всё зависит от конкретной задачи, главное правильно определять где фон (его надо игнорировать) и где значимые пиксели (вот их и надо искать).
По поводу создания изображений и помощи валенкам у меня тоже есть идеи, но это после поисковиков. По крайней мере нужно довести до ума. Обязательно хочу прилепить буфер из findString'а моего. Буфер изображений, чтобы они не дергались с винта это реально хорошая весч. Прикрутить возможность задать папку, а не конкретное изображение, дать возможность оверрайдить параметры поиска (если ищет всю папку) исходя из имени файла. Все это оптимизировать и вылизывать. Работы вагон. Но чет меня прет) Сижу с удовольствием неспешно тесты гоняю, переписываю все.
Из забавного. Речь пойдет о чтении из памяти данных, которые уже существуют, их размер известен. Т.е. сознательно двигаются указатели, в т.ч. происходит выход за пределы массива (мы уверены, что за пределами массива все так же находятся данные). При тестах инициализация массивов, присвоение начальных адресов были вынесены за пределы бенчмарка. Т.е. непосредственно время обращения в 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 нет.
Ну вот, зачем спрашивается лопатить фул хд. Разбил на 4 зоны, запустил 4 скрипта и собирай урожай.
ну вообще типа да, но типа нет. Поуму нужно делать потоки. Пока не берусь. Хотя бы потому, что не очень хорошо представляю сам для себя, как все это должно собираться. На данный момент есть четкое правило - все найденные результаты упорядочены слева-направо и сверху вниз, так же есть ограничение на количество найденных результатов. При разбиении на части может получиться путаница, если делать сортировку, то нужно дожидаться окончания обработки до некоторой позиции N в которой собрано необходимое количество изображений и т.д. Пока я для себя в голове не решил, как это будет выглядеть правильно и какое поведение будет наиболее востребовано и наиболее нативно ожидаемо для пользователя.
// Cirus подогнал getimage нормальный для работы через хендл.
rmem ("unsigned char*",i)[2]
Я собственно так и делаю в финдах, тоже в своё время тестировал.
Естественно потоки просто напросто правильнее.
А вот про getimage - очень интересно. Если поделитесь, буду рад. Во всяком случае у меня именно он является 100% якорем.
Автор Cirus
--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))
Понял лишь в общих чертах. Вопрос, а в файл сохранить картинку можно?
Я правильно понимаю, что мне надо добавить возврат значений из функции? В примере обработка ведётся прямо внутри функции.
Вот нет образования, вот и гадаю на кофейной гуще.
Адрес битовой маски фиксированный?? Или я не догоняю?
Короче, если довести до ума, то никаких параллельных скриптов не надо. А вообще конечно меня сильно удивляло, КАК механический винт маслает 60 МБ в сек. и как получить пару КБ с экрана - такие тормоза, понятно, что вопрос подхода к проблеме.
Насколько я всё-таки понял, всю аналитику надо вести внутри функции. Или не так??
Пока кроме скорости ничего не тестил, по идее он вроде битовую маску как раз возвращает.
ОК. Буду ждать новостей. Cirus-у отдельное БОЛЬШОЕ спасибо.
Накидал кучу вопросов Cirus'у и надеюсь, что он меня не пошлет) В целом все готово. Осталось закрыть по getimage вопросы и отдам код на тесты и критику реализации. Вообще код когда-то выглядел очень красиво и стройно... Пока не прошли оптимизации... Но тут функция относительно высоконагруженная и зачастую требуется максимальная скорость реакции и снижение нагрузки на камень, так, что пусть лучше выглядит не очень, но зато работает, как надо.
Собственно то, как вот оно есть.
-- 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.
--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()) -- скрин с рабочего окна (может быть перекрыто)
--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()) -- скрин с рабочего окна (может быть перекрыто)
Дарк, прочитал мануал. Что точно нужно добавить - это шаг поиска. Что возможно тоже может понадобиться - направление поиска. В остальном вроде всё как надо.
Cirus - спасибо большое. Пока ещё толком не тестил новые вещи. Чуть позже поделюсь мыслями.
Для чего нужен шаг? Например для поиска активной радиокнопки ("галочки", и т.п.)
Как весело провести вечер и утро.
А знаете ли вы, что если получить указатель на локальный C массив и выйти за пределы видимости, то сборщик мусора в дебаге ее не почстит и ffi.cast будет работать, а если выключить дебаг, то переменная будет почищена и все отвалится? Включаешь дебаг - работает, выключаешь - не работает. Белочка рядом =)
Попробовал новый getimage. Работает намного быстрее. Прирост скорости зависит от зоны сканирования - это ОЧЕНЬ хорошо, так как нет принципиального тормоза.
Как я сейчас делаю. Получаю образ и потом на его базе ищу разные объекты (их немало). Потом чищу память.
А вот как прикручивать новый getimage на практике пока не знаю. Вариант пока только такой. Для каждого поиска используется свой getimage, в принципе по скорости всё приемлемо. НО. Иногда мне требуется работать с 2-мя образами, анализируя изменения (до и после). Как в таком случае? И опять же ... выполнил поиск одного объекта. Начал выполнять другой поиск, а на этот момент сменился кадр в окне. И по факту ищется уже при других условиях. В некоторых ситуациях это крайне нежелательно. Короче надо думать как это привести к единому знаменателю.
Вот я и думаю. Может сделать массив bits глобальным. И пусть он потом просто обновляется. Выделить под него память исходя из возможного максимума и собственно всё. Если я где-то сильно не прав - поправьте.
Насколько я понимаю, по уму надо бы возвращать адрес массива bits, а по ненадобности освобождать память (как всё это делать я не знаю).
Бэкапу пару дней. !@#$ notepad++
Эскизы прикрепленных изображений
А тут как дела? Подвижки есть?
Захват изображения. Вот так вроде похоже на правду. Пока никуда еще в финды не подсовывал. Утрачено реально дофига, но там было очень много моментов, которые надо было понять и осознать. Восстановлю, конечно, шустрее, чем писать с нуля.
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)
Если угодно - сглазить не хочу.
Блокнот реально подвел тупо из-за того, что обновился. У меня по дефолту в бекапах чуть более, чем все =) Разные винты, разные верссии, что-то в оффлайн, что-то допом в облака. Произошло старое любимое "не было печали - апдейтов накачали".
Ну вот, я явился возмутителем спокойствия и благоденствия. Просто я ставил вполне себе понятные цели. Если мой глаз что-то видит и мозг даёт команду ткнуть куда-то, то это точно можно запрограммировать. Вопрос в степени сложности алгоритма (моё любимое занятие - придумывать). И именно из-за объёма вычислений и нужна эта самая скорость. Но даже реализация этих целей меня не устраивает. Я иду дальше, на основе собранной статистики - я пытаюсь ПРЕДСКАЗАТЬ то, что глаз ещё не видит. И это получается (цель ещё не появилась, а по факту она уже уничтожена - если очень грубо описать сей процесс).
Получается что это надо только мне. Но ради меня точно не надо напрягаться - для меня это просто хобби. Я своё давно отпрограммировал.
Попробовал сохранить картинку. Ничего не получилось. Пробовал всяко. С потерянной единичкой на ширине и высоте, без единички, 32 битами, 24 битами - результат один. Совершенно не догоняю почему зашит жёстко адрес 0x4d42 при инициализации заголовка файла.
Создаёт файл 14 байт. Доступ к нему действительно отсутствует, пока не закроешь Пилот.
Пробовал в разные папки. Результат тот же.
Пишет только заголовок.
Всё от имени администратора. ???
Поменял местами, сначала пишу инфо выход - 40 байт. Значит блокирует после первой записи в файл. Что за хрень, непонятно.
Последовательно пишу заголовок. Перегружаю Пилот, инфо, перегружаю, маску. Потом всё в один файл - получил картинку 32 бита, 300х300. Всё дело в доступе. Ну и действительно единичка потеряна, должно быть 301х301.
Дарк, что-то я не понял как пользоваться твоим имиджем. Насколько я понимаю информация хранится в bitmap_address (по аналогии с bits). А как вытащить то?
Значит так. Версия из активной разработки. Дальше функции нужно обязательно развернуть в тело финдколора - это существенно ускоряет работу. Но при этом вполне очевидно получается дикий свинарник, особенно если учесть необходимость дублирования циклов под каждый из типов поиска. Поэтому я прошу потестить именно в таком виде. В идеале было-бы что-нибудь типа теста, как я делал под 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()
--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
Ни хрена себе ты наворочал. Тут месяц надо разбираться. Попробую. А надо то было просто добраться до битовой маски. Да пихнуть бы в файл без извращений.
ext.lg - перекочевало из другого скрипта, в рамках color.lua поставляться не будет, вероятно будет жить в другой бибилотечке. Это из моих загашников.
Конечно моё мнение субъективно. Но вот зачем вообще нужен deviation? В имидже понятно, а в колоре зачем? Можно абсолютно точно задать требуемый диапазон поиска. Ради совместимости со старым хламом?
Может не доглядел (тогда ткни меня носом), но не нашёл поиска по RED-BLUE, RED-GREEN. Поиск по разности каналов куда как важнее deviation. Могу привести просто кучу примеров, когда это необходимо. Очень грубый пример, просто для понимания. Ну вот навёл прицел на мишень - прицел стал другим (обычно ярче) и, как правило, он легко ищется в обоих состояниях именно по разности между каналами.
Да и color не нужен. Тот же винд. А для совместимости проще сделать финд-олд.
У меня реализация именно такая, могу вызывать или как if findcolor (если нужно просто проверить) или b,k=findcolor (если нужно получить конкретные результаты)
Опять же сугубо лично моё субъективное мнение. Чем проще и элегантнее, тем лучше. Появились новые технологии - отказывайся от старых. Извозчиков оставим для туристов. А тащить за собой весь хлам совместимости - только ошибки и коллизии плодить.
По разности каналов не делал. В достаточно большой мере метод 'r' подразумевает эту разность, равно, как и 's' в сочетании с некоторым диапазоном.
Да я всё понимаю. Поэтому и не берусь претендовать на что-либо. Делаю всё чисто для себя. Просто ты просил мнение - НА получи.
Если есть любые идеи, как оно должно быть - готов изменить/поправить. Единственная просьба - максимально конкретно объяснить свое вИдение.
Да, кстати. Что-то у меня не получилось сохранить картинку твоим имиджем. Что не так делаю? Кинь конкретный примерчик.
--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
Вот объясните мне почему так? Просто хочу понять, чтобы быть в теме.
Дарк, у тебя отрицательное значение высоты:
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} })
Спасибо, ну примерно так и подозревал. Плохо ничего не знать.
Соответственно 32 битовая модель не требует контроля длины строки, правильно понимаю?
Ну в смысле кратности 4.
speed = speed + os.clock() - t
Лишняя строчка?)
Эх! Понять бы ещё почему у меня проблемы с записью в файл bmp. Попробую-ка я комп перегрузить, наверное уже месяц маслает круглые сутки.
Перегруз не помог. Нарушается у меня доступ к записываемому файлу. Очень странно, вроде, ну чего уж может быть проще.
Можно удалить. Мой код тестов:
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)
Если не указать [deviation] вылетает с ошибкой .lua:489: attempt to index local 'v' (a nil value)
Если координаты указать массивом то вылетает с ошибкой .lua:614: bad argument #2 to 'min' (number expected, got nil)
У меня одного если не указать "abs" то ищет в фулхд, хотя указаны конкретные координаты?
Лог без abs
268554272 1921 1081 5764
268554272 273787983 5764
12398680 63 60 192
12398680 12410199 192
Это сделано исключительно для совместимости со старым кодом. Предполагается, что в дальнейшем основным методом будет забор по хендлу, либо если будут проблемы, то через метод 0, который по сути тоже самое, но не на приложение, а на весь экран. При методе 0 никто не мешает указать стартовые координаты приложения и его размер. Возможно имеет смысл сделать это более легким для использования(скрыто получать координаты приложения и его размер) и врубить там проверку abs.
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)
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 от фулл хд.
Я его код не тестил. Мне скрины на тот момент были не нужны совсем, а одновременно заниматься двумя разными вещами я не могу. Сегодня гляну.
Накидал скринилку. Фиксировные битовые константы заголовков сделаны сознательно с целью быть полностью уверенным в том формате, который будет использован. Сюрприз, но 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
a, w, h, l = getimage (x1, y1, x2, y2, method, abs_crds) -- getimage игнорирует x2, y2
internal.getimage_orig = 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
Тест старого и нового имиджа по хендлу
зона 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
Круто я когда-то лажанул. Убрал объявление getimage = ext.getimage, а в коде не заменил. И где должен был использоваться новый захват - получился старый гетимидж.
Хотя тестить в lua - гиблое дело. Можно лишь приблизительно судить о быстродействии. Вроде делаешь каждый раз одно и то же, а разброс по времени бывает ну очень даже приличный.
Я не стал ничего менять, пусть будет старый, пусть будет новый. Кто его знает этот ваш новый - посмотрим ещё будет ли вести себя примерно.
При желании одним росчерком пера можно сразу поменять во всех файлах одновременно старое на новое. Не вижу тут никаких проблем.
getimage 100 итераций
вызов + удаление изображения FullHD
старый 4.80
новый 1.65
вызов + удаление изображения 1/100 FullHD
старый 0.93
новый 0.08
Абс (метод 0) только с новым ибо я не уверен насколько корректно можно вызвать захват по хендлу с абс в рамках старого синтаксиса (не документировано)
FullHD 6.0240000000013
1/100 FullHD 2.5
Дарк, спасибо за скринилку. Всё, на сегодня хватит. Всё буду смотреть завтра. Наплевать на развитие, заголовок подправить плёвое дело. Я ж говорил, у меня Пилот блокировал файл. На крайняк всегда можно посимвольно в lua записывать. Хотя честно, я просто не представляю как до такого кто-то додумался. 30 лет назад и то всё круче. Тот же Паскаль из того же буфера, хоть на низком, хоть на высоком уровне. Только в lua такая дебильная работа с файлами.
Нужно добавить возврат координат найденного цвета, можно еще кол-во
Функция возвращает таблицу найденных цветов:
x y цвет1 цвет2
x y цвет1 цвет2
x y цвет1 цвет2
x y цвет1 цвет2
под цветом имеется ввиду диапазон в который попалось. А вообще спасибо, нужно переделать под:
x y найденный_цвет исходный_цвет(не диапазон)
Проблема в том что она возвращает x y относительно координат поиска а хорошо бы возвращать реальные координаты куда можно сразу кликать.
Вот для этого тесты и есть =)
Поправлю. Спасибо.
Супер, спасибо за труды! Ждем findimage
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_*
Так же хотелось бы мнение услышать.
За всё время шаг поиска использовал только 1 раз. Сканировал поле для игры 3 в ряд. Учитывая, что в последнее время в играх одни и те же картинки отличаются, то такой способ уже не актуален.
--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)
То ли лыжы не едут то ли спать пора. В общем что у меня выходит, 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)
return cc[1][3]
Кстати Dark, с новым getiamge ломается твой FindString) он при проверке хендла сравнивает число со строкой потому что
default.window = "workwindow"
Пофикшены утечки памяти.
По поводу шага. Вот внизу у вас закладки с номером текущей странички. Как узнать которая активна?
Можно узнать без шага? Конечно можно. А с шагом? По скорости однозначно быстрее. Реализация? внутри поиска + 1 оператор. Я сам использовал шаг ну раза 4. Это было в Пилоте. При нынешних скоростях возможно шаг и не актуален. Но я у себя оставил, одна строчка кода мне нисколько не мешает.
Я 100пудово могу нарисовать задачу, где без шага вам придётся лопатить массив полученных пикселей, используя кучу математики (div-ить, mod-ить и т.д), а с шагом просто найти ПЕРВЫЙ удовлетворяющий условию поиска пиксель.
А вот color ну реально не нужен. Обычное упрощение, расчитанное ну уж на совсем неопытного пользователя. Никакого реального функционала он не несёт.
А вообще Дарк, по поводу шага, можно действительно на него плюнуть. При нынешних возможностях ничто не мешает организовать этот самый шаг в цикле и просто вызывать в цикле финд столько, сколько нужно, хоть десятки раз. Это действительно было актуально, когда в Пилоте не было поиска в памяти и каждый вызов финда был на вес золота.
Нашёл я причину моих мытарств, так и заставили штудировать СИ.
--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]])
У меня не работает. Почему не знаю.
У меня выведет в лог только pass1 и файл будет 4 байта и заблокирован.
А уж почему так?? Я в этих СИ-шных типах плохо ориентируюсь.
Интересно конечно услышать ещё мнения других. Это только у меня так?? Если только у меня - тоже встаёт вопрос, а что у меня не так.
Ошибок не будет. Файл должен быть 10 байтов. Должно 4 раза вывести в лог "pass" и файл должен удаляться не выходя из Пилота.
В моей скринилке я пока шаред не врубил оно блочилось. Вообще оно как бы странно, но каких-то глобальных проблем от шареда я не вижу. Во-первых кто будет в скрины писать в несколько потоков? Ну хорошо, кто-то наваял мегакод, решил фигачить в одинаковые файлы скриншоты с разных скриптов... Ну запретим мы шаред - выбьет еррор. Легче станет?) Не думаю...
почему OPEN_ALWAYS а не CREATE_ALWAYS?
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.
Х. его знает. Я причину своих неудач озвучил чётко и понятно. Может кому-то это поможет. Разбираться во всех тонкостях сего процесса не имею ни времени, ни желания. Помню как в своё время чего только не изучал, и то, и сё. И кому всё это теперь нужно (кому теперь нужен ремонт магнитофонов). Плюнуть и растереть все текущие проблемы. Работает и слава богу.
Ну вот и разница. А у меня весь этот хлам висит на WIN 7.
Удивляет другое. Запись в файл существует со времён сотворения мира. Чего там может быть нового?
Попробовал новую версию Пилота - результат тот же. 4 байта и блок файла.
Фиксы.
В общем что мы имеем, игра:
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)
local dwWritten = ffi.new('DWORD[1]')
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
getimage = internal.getimage
getimage = internal.getimage
То ли я что-то не так делаю то ли не помогло
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)
Добавил ещё один вариант сохранения изображения используя fwrite вместо WriteFile.
https://forum.uokit.com/index.php?s=&showtopic=70636&view=findpost&p=440290
Что по скорости?
// Пока лепил имидж потестил разный тип deviation сохраняя картинки который получаются после применения deviation - достаточно любопытно выглядит. 'a' - чистый brightness, 'r' вроде как контрастность получается =)
Ето. Я не забил) Я наворотил Процесс идет.
Цук вроде буфер доделал... Сколько он у меня крови попил... Зато теперь 1 конвретация фул хд изображения по методу r занимает 36мс, 1000 конвертаций 64мс. В общем генерация изображений по a, r готовы. Осталось накатать таблицу на s (там не изображения) ну и перебор сгенерированных изображений.
Супер! Ждем! Ну я уж точно!
По сути первый запуск. Ну почти первый. Вроде даже нашло.
Синтаксис почти аналогичен старом финдколору, надо будет чуть-чуть дошаманить.
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
В ext.findimage нет объявления переменной t.
Если картинка по указанному пути не найдена, то выдаст ошибку.
color1.lua:928: attempt to perform arithmetic on global 'h' (a nil value)
color1.lua:1153: attempt to index a nil value
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
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
DarkMaster, а ты уверен что у пилотного findimage поиск просто перебором? С маленькими картинками может и будет быстро искать, но чуть побольше уже сомнения. К примеру возьмём картинку 40*25, 1000 пикселей, при 50% точности нужно проверить половину, т. е. 500 пикселей чтобы понять что в проверяемом месте картинка не найдётся. Т. е. для FullHD 1920*1080*500 больше миллиарда пикселей надо проверить.
Хз. Переделал на ffi.cast по адресам - разницы особо не увидел... Недопонимаю в чем проблема.
Вобщем у меня тесты на фул хд и искомом изображении 46*41. Искомое изображение в одном экземпляре в правом нижнем углу, т.е. находится в самом конце. При 90% точности время поиска занимает 10 секунд. Во время тестов выяснелось следующее... Каждый новый счетчик добавляет примерно 0.5 сек, само условие со вложенной проверкой на достаточность количесво пикселей занимет 9 секунд. Если выкинуть проверку на достаточность пикселей, то время выполнения сокращается на 5 секунд.
Что со всем этим делать пока не понятно... Если выкинуть весь хлам и оставить один счетчик, то выполняет мгновенно...
Всем Гуд! Дарк, у меня не было времени тестить новые фишки. Да и надоели мне все эти тесты, я собственно говоря - практик, мне интересно смотреть методы, которые вы используете, чтобы потом использовать их самому. Я не большой сторонник МЕГА проектов. На мой взгляд лучше сделать несколько функций (old, new или вообще дать функциям новые имена), чем всё запихивать в одну. На мой взгляд ты торопишься с "похоронами" имеющихся финдов. Давай для начала сделаем loadimage, чтобы хоть какая-то была работоспособность, а уж потом хоронить всё старое. Да и saveimage я бы доработал. Ну для себя точно буду переделывать. Мне нужно, чтобы сохранялся в файл не ВЕСЬ образ, а его конкретная зона. Ну вот пример практика ... Сделал образ ... поискал одно ... поискал второе ... а на третьем, что-то пошло не так и мне было бы намного удобнее сохранить в файл именно эту область, чтобы потом не шариться в редакторе высчитывая координаты.
По поводу финдимиджа стандартного (текущего). Я давным-давно от него отказался. Я не знаю причин, но там просто убийственные тормоза, если увеличиваешь deviation. У моего варианта нет никаких зависимостей от этого параметра. У меня зависимость только от параметра accuracy и это понятно, потому что поиск выполняется до превышения "ошибок". Я использую 3 параметра: погрешность (deviation) нужна при малых значениях RGB, точность (% отклонения цвета) и аккуратность (похожесть картинки) если например часть картинки перекрыта другой картинкой.
И как уже говорил, я использую свой (кривой, нестандартный, но очень эффективный) метод. Я формирую (перед тем как пихнуть её имиджу) искомую картинку по своему усмотрению. Может искаться и фон и не фон, количество искомых условий не ограничено (каждое условие может иметь свои параметры отклонений), на скорость никак не влияет. В частности при таком подходе поиск символов (цифр и букв) становится намного проще, точнее и быстрее.
Проще говоря создаётся матрица {X,Y,R1,R2,G1,G2,B1,B2} и ищется эта самая матрица. Если подсунута не матрица, имидж создаёт её сам, а если нет использует подсунутую. В большинстве случаев, особенно критичных к скорости, используются уже именно готовые матрицы. Они загружаются у меня при старте из бинарного файла и всё летает на ура.
Для меня задача №1 loadimage. Без него никак не обновиться по полной программе. Думаю сам проковыряюсь долго, хотя вроде общие принципы мне понятны.
Чтобы было понятнее про матрицу. Ну вот например поиск цифр 7 и 2 осуществляется поиском (сравнением) всего 2-х пикселей. У семёрки 2 крайних верхних, а у двойки 2 крайних нижних, надеюсь понятно объяснил.
Мое текущее безобразие.
По поводу не надо было... Одно дело надо, другое возможность. Иногда кушать еще надо( Возможность появилась, я занимаюсь. И ты сразу, как почуял)
Прикрепленные файлы
fi.bmp ( 90 байт )
Кол-во скачиваний: 75
fi_large.bmp ( 5,66 килобайт )
Кол-во скачиваний: 46
scr.bmp ( 5,93 мегабайт )
Кол-во скачиваний: 55
Хотя чего там тестить. Просто кину кусок кода который собственно и ищет, всё по минимуму. Чисто для примера, сорри без комментов, с кривыми именами переменных. Сделано давно и просто лень было облагородить.
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
Из кода видно, что чем меньше матрица pic[0][0], тем быстрее будет поиск. НАМНОГО быстрее если лопатить большие зоны. В этом вся и фишка. Хотите скорость - надо напрячь мозги. Лень, ну тогда будет медленнее. Каждый выберет для себя компромисс сам.
Из твоего bmp-шника не совсем понятно как искать. Насколько уникальна картинка, можно искать по красному, можно по зелёному. СМЫСЛ в чём?? Чтобы увеличить скорость, надо понять уникальность картинки. И если например такого ярко красного пикселя нет нигде, кроме как в этой картинке, то можно искать тупо по одному пикселю, а ещё тупее - найти его колором.
Соврал, по одному не получится, а вот по ДВУМ - легко.
И повторюсь. Я могу искать не только значимые пиксели (скажем красные), но и фон. Например посередине от этих двух красных однозначно присутствует светлый пиксель (скажем RGB данного пикселя все значения не менее 100) или например бесцветный пиксель значения RGB отличаются друг от друга не более, чем на 30 единиц. Нет никаких ограничений для фантазии юзера.
Ещё раз повторюсь. Если мне например не нужна скорость, ничего я не делаю, лень ... никаких условий ... задал погрешность и всё. А при желании можно задавать сколь угодно сложно комбинированные условия поиска (по которым и будет создана матрица поиска).
И вот чтобы искать такие бесцветные пиксели и нужны RED-BLUE , RED-GREEN , GREEN-BLUE
Хорошо, сейчас попробую, протестирую. Мне по барабану какой девиэйшн, всё будет искаться одинаково. Спазу скажу, ни о каких секундах не может быть и речи. Сейчас попробую.
--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 сек
Брать с искомой картинки первый пиксель и искать его на скрине, если нашёлся, то уже искать саму картинку в этом месте. Если точность не 100%, то брать второй пиксель и снова искать и т. д. пока хватает точности. Для fi_large.bmp этот способ точно будет быстро искать. По сути сначала вызываем findcolor и уже потом используем findimage в найденных координатах.
Это искались ВСЕ голубые пиксели (r=0)
Код чисто для примера, запускать не надо (для новичков), без модуля понятно работать не будет.
Погоди Дарк. Надо смотреть. Что-то я не доглядел. Не глядя пихнул. Там оранжевые - это что? Паразитные что-ли? или фон?
Понял, сорри, не то делал.
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 отличается от того что на скрине. Там оранжевый кант в левом углу совсем другой.
Я не спал уже хз сколько, ухожу на отдых в уверенности, что завтра будет понимание и прогресс =)
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 . Теперь уже в прошлом!
Так что разбирайся Дарк, что-то у тебя не так. Кусок кода я дал, сравнение всех пикселей картинки с предельными значениями, собственно ничего тут такого нет. Быстро даже не используя моих хитростей.
Естественно всё это СИ-шные массивы.
Я знаю где тормоз. У меня матрица содержит сведения ГДЕ находится искомый пиксель. Я не лопачу всё подряд, а смотрю конкретную позицию. Вот за счёт этого и скорость. Дарю идею - пользуйся. Хотя ещё полтора года назад дарил.
n=ins+pic[i][6] -- это индекс в битовой маске - это ключ скорости.
Если будет надо могу дать весь код, там наверняка много кривизны, но мысль понятна.
Держи всё целиком. Лепил из кусков модуля, мог и накосячить, но вроде работает.
Только пути к файлам переправьте.
--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
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
DarkMaster, когда давно, по-моему ты и задавал вопрос как определяется точность, с которой была найдена картинка. Кнайт подтвердил мой предположение что точность определяется после поиска. Из чего можно сделать вывод что всегда ищется вся картинка. Т. е. в цикле нет проверки что дальше нет смысла искать, только счётчик сколько совпало пикселей. А уже после поиска проверка достаточно ли совпало пикселей.
Точность поиска не влияет на скорость в пилотном findimage, т. е. в нём явно нет тупого перебора всех пикселей. Также sutra писал что поиск тормозит при большом отклонении в цвете, что вполне логично, находится много пикселей и надо во всех проверить есть ли в этих местах картинка.
По сути надо как минимум 2 варианта поиска. Тот что сейчас сильно зависит от точности, но почти не зависит от отклонения в цвете. Предложенный мной вариант не зависит от точности поиска, но зависит от отклонения в цвете.
Кстати 1920*1080 ~= 2 миллионов, а не миллиардов :-P
Друзья, вы меня конечно извините, но вы не над тем голову ломаете. Давайте по фактам.
Чем больше диапазон поиска (не важно какими параметрами он расширен), тем дольше будет поиск и ничего тут ускорить нельзя. Дарк, твой пример для тестов не годится. Чтобы реально нагрузить поиск, весь экран должен быть напичкан похожими картинками. И вот для такого случая я бы привёл вам реальные примеры использования моего финда, увеличивая скорость поиска в разы.
Далее ... Одну картинку найти - это не проблема. Давайте исходить из того, что нам придётся искать их сотню. Я подправил немножко имеющийся тест, подсовывая для поиска в функцию не адрес, а СИ-шный массив. Скорость выросла ровно в 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
Сколько if'ов в секунду у тебя выполняется?
Единственное, что можно было бы добавить в мой (ну и в твой наверное тоже) алгоритм - шаг поиска картинки. Но мне это не надо, а всем остальным, как я понимаю мои идеи не интересны. Живой пример. Cirus кстати его давал. Нужно собрать ресурсы (золотишко) с домиков. Я могу легко и однозначно найти золото синтетической картинкой из 3-х пикселей. Но будет искаться несколько картинок на одном фрагменте золота, а вот если после первой найденной, делать смещение на n-е количество пикселей - этого можно избежать. Короче, найти можно абсолютно всё (ну почти всё), надо всё-таки двигаться в технологичном направлении. При подробном описании возможностей функции, с конкретными примерами, даже не самые продвинутые юзеры (хотя Пилот не для тупых) будут в восторге.
А тебе Дарк - это всё внедрить просто сам бог велел. Это же твой хлеб - реальные деньги. Нарисовал юзеру скрипт, который всех кладёт на лопатки - юзер тебя отблагодарит. Ну кроме меня, меня вам не уложить.
Если смог чем-то помочь, с вас новый loadimage , а то боюсь, что я с моим знанием СИ такого наваяю...
Пропуск пикселей - это задача для мозгов. Тут ты никак не победишь тормоз. Глупо, наивно и бессмысленно искать с потерей 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 ифов
Я не знаю как тестировать старый, только если искать изображение в редакторе. А как задать цикл? Если там всё время подгружается файл? Без цикла, будет неточный тест.
Если хватит воли и смогу победить свою лень, если потестирую старый - отпишусь.
Дарк, переделал алгоритм. Скорость поиска - нуль. Чем больше % пропуска, тем даже быстрее. Сейчас некогда, через часик освобожусь, слегка подправлю код и выложу результаты. Пока ясно одно, наши алгоритмы работают.
Поторопился я, всё не так просто. Да, если искать только одну картинку, то вроде всё понятно, а вот если несколько, тут у меня пока не получается. Надо думать, но наверное уже не сегодня.
Прошу прощения, я ОЧЕНЬ сильно затупил. Думаю что в поставленной задаче ничего ускорить нельзя. Я вспомнил. Я даже вроде об этом спрашивал в теме про Пилот. Я нашёл в своё время косяки у стандартного финда и вроде (точно уж не помню) это и было как раз при поиске нескольких картинок, когда они накладывались друг на друга. В общем чего-то он не находил. Я не знаю какой там алгоритм, возможно какие-то методы исключения зон из последующих проверок. Короче господа, вы уж как хотите, а я буду использовать свой алгоритм, проверенный, абсолютно понятный, с предсказуемым результатом. А пропускать 250 пикселей на тестируемой картинке, я не вижу этому никакого практического применения. Ещё раз сорри за дезинформацию.
Прикручена функция
-- 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
Спасибо. Да я сам не знаю чего я хочу. Сам дотункаю, так что не парьтесь. Надо будет наверное просто глянуть чем отличается write от read. Я же для себя ковыряю. Всё переделал. Мне больше чем ХД не нужно. Сделал 2 глобальных массива CI[1] и CI[2], сразу выделил им по ХД, в функцию даю параметр в какой массив битовую маску получать и всех делов. Я так подумал, десятки раз в секунду у меня происходит захват экрана, зачем мне абсолютно ненужные действия, кроме тормозов никаких плюсов. Почесал конечно репу, пока не вспомнил про твой минус высоты, а то всё с задницы начиналось. Я ведь по факту и получал по адресу этот самый массив, короче убрал лишнее действие.
Вот только пока не определился с чем лучше работать с 32 битами или по старинке с 24.
И думаю всё про Кнайтовский алгоритм. Прямо даже зацепило. Наверное как-то построчно идёт анализ, уж очень хитро. Хотя, как и говорил, есть у меня подозрения на косяки. Я давно экспериментировал с ним, могу конечно ошибаться, как говорится не в первой. Но вроде как финд косячил именно на смешении картинок. Помню я нарисовал что-то типа забора, ну в общем, куча повторяющихся элементов, получалось как бы картинка на картинке. И на этом большом заборе искал его куски, естественно их находится много, и вроде как он неверные координаты выдавал. Хотя конечно интересно бы глянуть на алгоритм.
НО. Зацикливаться на нём точно не надо. Минусов в старом в разы больше. По моему опыту, мне вот как раз не надо делать пропуски. Всё с точностью до наоборот. Мне надо распознавать объекты в разных состояниях. А пропуски будут игнорить эти различия. Повторюсь, не вижу никакого применения, кроме наложения объектов, но как правило - это небольшие объекты и всё работает быстро. Во всяком случае я не видел особых тормозов, когда распознал ВСЕ твои мишени, всё равно, с новым имиджем получается однозначно быстрее старого финда.
Спасибо вам друзья огромное!! Лично мне всего хватает и скорости и функционала. Вспоминаю, как извращался с Пилотом на родном ему языке - ужас. А нынешние технологии, для меня это просто прорыв.
А тебе Дарк, как раз стоит подумать про забор. Очень интересные там могут получаться комбинации. Почему я и говорил про шаг поиска и при поиске картинок. И хоть у меня пока таких проблем не возникало, но, во всяком случае практическое применение этому я вижу.
Конечно если вы вытащите алгоритм Кнайта - это будет круто, но я сколько не думал, не могу понять, как можно найти все элементы забора, которые присутствуют с минимальным смещением и по горизонтали и по вертикали, кроме как перебирать ВСЕ варианты.
У меня остался один вопрос, может подскажете. Как резвенько скопировать часть СИ-шного массива в другой СИ-шный массив. В Паскале была функция move, которая позволяла это делать. Выделять память как я понимаю надо использовать malloc , удалять free.
--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])
Спасибо Cirus! Скоро буду СИ-шником.
Это не Си. Смотрите memcpy() функцию.
void memcpy(void* Destination, void const *Source, unsigned int Length);
Не, malloc - это указатель делает, нахрен не нужен. Делаю new
void memcpy(void* Destination, void const *Source, unsigned int Length);
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
Память освободится при завершении работы скрипта (из контекста не понятно - это часть или весь скрипт) либо если память будет забита и в игру вступит сборщик мусора. Но тогда и скрипт подвиснуть должен.
https://stackoverflow.com/questions/2963898/faster-alternative-to-memcpy
Точно, не чистится, сделал задержку после ифа , посмотрел на память - не очистило. Тогда так нельзя. Тогда что? только через malloc?
Инглиш не для меня. Я в советское время учился и мы считали, что этот буржуйский язык нам никогда не пригодится.
Мне просто показалось, что rmem всё-таки подтормаживает процесс, почему я и использую СИ-шные массивы. Как то всё уж больно хитро. Вот раньше, как просто у меня было. getmem p-адрес p^[25] указатель на конкретные данные freemem - свободен. А тут кругом препятствия.
Задача то простая. В цикле (скажем 1 сек.) ... Сканирую экран ... поиск объектов ... хотелось бы получить копию битовой маски (небольшая) ... если всё штатно освободить память, если нет сохранить все кадры в файлы и освободить память и это всё в одном большом глобальном (почти вечном) цикле.
В чем смысл так часто очищать память ? На производительность это хуже только повлияет.
Да понял я уже это Дарк, спасибо. Кстати в дремучем Паскале на каждое new было своё dispose. Жаль, что здесь нет такого.
Это требуется только в критичных (надеюсь пока критичных) ситуациях. Но крутится всё в одном глобальном цикле. Никакой памяти не хватит если её не чистить.
Дарк, не будет проблем. Очистка памяти не дольше её выделения. Всю жизнь все так программировали.
Я всё думал про алгоритм Кнайта. Вы почитайте историю развития. Что там написано - ищет по уникальному для картинки цвету (абсолютно прагматичный подход), но он не должен быть ничем перекрыт. Погрешность прикручена уже позже. Думаю весь секрет скорости зарыт здесь. Возможно осуществляется и какой-то анализ экрана (то на чём ищется картинка).
А вот тут и возникает вопрос, что лучше, иметь скорость при использовании похожести, но не иметь 100% достоверности или наоборот. Практические аспекты использования имиджа в разных ситуациях я уже приводил. Принцип Кнайта очень даже хорош. Я пытаюсь делать собственно то же самое - ЗАДАТЬ УНИКАЛЬНОСТЬ для искомой картинки, но совершенно иным способом.
Я проделал небольшой эксперимент. Запоганил слегка тестовый экран Дарка на 10%. Фоном принял то же пиксель, что для финда Кнайта. У моего алгоритма похожесть 90% (на 91% не найдёт картинку), а у Кнайта - это 85%. Неплохой вопрос почему?
Это если у Кнайта делать погрешность 0, а вот если делаю погрешность 10%, то и у Кнайта тоже похожесть становится 90%.
Ну в общем, по крайней мере лично для себя, я вопрос решил. Не буду я больше ничего изобретать. Буду пользоваться своим. Надо просто грамотно организовывать поиск и никаких проблем не возникает.
Я во всём люблю ясность. Любые ... если ... возможно - это не для меня. Всё должно быть абсолютно точно. Искаться то - что должно искаться и никак иначе. Но это лично моё субъективное мнение. А вам други - удачи, творческих побед и здоровья.
У кнайта отображается точность при которой алгоритм посчитал достаточным для совпадения. Т.е. если мы зададим точность, скажем, в 80%, то первый же пиксель который перейдет границу в 80% будет последним. Т.е. имея, скажем, картинку из 100 пикселей и точность в 80% ты никогда не увидишь, что она нашлась с точностью 81%.
Кстати, вырезалку из образа, тоже таким же способом.
sutra, принципиально делаем две версии одинаковые?)
Дело всё ещё в том, что я совершенно не понимаю принципы работы lua. Что и чего там делается я не знаю. Изучать всё это - мне жаль убивать своё время, я далеко не молод. Тупо ориентируюсь на готовые решения (примеры) и на интуицию. Вот пример. rmem - это что? Я не знаю. Это аббревиатура доступа к данным? или полноценная функция? Если функция (о чём я интуитивно подозреваю), тогда это однозначные тормоза, т.к. уже задействуется память, т.к. используется стек, хотя х.з. как сейчас всё реализуется, может всё уже распёхано по памяти.
Раньше я чётко понимал ... передал в функцию параметр как var - передал адрес, без него - будет создана новая переменная, почему и было глупо передавать массивы как параметр, а не как переменную. Почему и используется буфер (даже понятие придумано конкретное) при передаче массивов данных ... Ну в общем ты меня понял, примерно так.
local rmem = ffi.cast
--lua
local print = log
print(123)
Дарк, давай уж приходи к единообразию. Если уж начал облагораживать меняя
math.floor(w*3/4+0.75)*4 на math.ceil(w*3/4)*4
Так везде поменяй. Или ты хотел продемонстрировать ВСЁ многообразие подходов и методов решения.
Да брось, это всё просто крохи, для смеха попробовал.
for i=1,100000000 do
a=8%3
-- a=math.modf(8/3)
end
За то как красиво и элегантно, и запоминаемо, я до сих пор путаю modf и fmod
Добавлен loadimage, image_mirror_v(по вретикали переворачивает), image_copy.
Опять переработан буфер на 10 раз. Переделан saveimage, getimage и все, что только можно.
loadimage сделан по хардкору по оффсетам. Вроде как работать должно на всех версиях и офсеты никогда не менялись. Будет смысл хоть какой-то - можно переписать с получением всех заголовков (зачем мне пока не ясно).
Удаление изображений по image = nil пока не реализовано, только deleteimage.
Прикрепленные файлы
color.lua ( 68,94 килобайт )
Кол-во скачиваний: 57
for i=1,100000000 do
a=8%3
-- a=math.modf(8/3)
end
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
void* lpOverlapped
);
void* lpBuffer
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
void* lpOverlapped
);
const unsigned long l = 0;
WriteFile(l, l, l, l, l);
Не о том господа думаем. Про скорость. Мне вот совершенно не нравится такая вещь: j=j+1. В Паскале была такая незаменимая для счётчиков вещь как инкремент (inc и dec). Там отсутствует контроль выхода за допустимый диапазон типа данных, а он в нашем случае как раз и не нужен. Наверняка ведь можно прикрутить такую штуку и в нашем финде.
Что-то мы не можем заставить машину картинки искать. Я ни на кого не посягаю, пусть делает каждый что хочет, а я буду делать то что я хочу.
И у меня есть ощущение, что if-ы слишком медленны. 250 лямов выполняются 2 секунды. Наверное как раз на битовом уровне это и можно было бы ускорить. Собственно что надо, узнать попадает ли конкретное число в заданный диапазон (всего в пределах 1 байта). Число целое - всё должно просто летать. А этот lua кто знает чего он там сравнивает, какие типы там у него.
Вот ещё интересная вещь. Заменил if j==sim на if rawequal(j,sim) и получил тормоз более 15%. Было 1.98 сек. а стало 2.34. Хотя по логике вроде бы должно быть наоборот.
Не понимаю я lua, от слова совсем. Надо попробовать полностью всё перевести на СИ. Просто интересно посмотреть на результат.
Короче, компилятор тут мама не горюй, как он чего делает, совершенно не понятно.
Простенький тестик, для смеха.
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 секунды делает, если там ещё всё проще. Обычное сравнение. Сравнил - пошёл дальше. Вот вам и тормоза на абсолютно ровном месте. Или это так вайлы тормозят.
ОТМЕЧУ! Тестик на так называемом тормозе под названием процент.
Короче, однозначно надо прикручивать инкремент, наверняка это можно сделать. И очень внимательно пересмотреть всю математику циклов.
Или он так долго шарится по памяти. Ведь на порядок медленнее в финде.
Тестанул цикл в цикле - результаты не изменились
for k=1,100 do
for j=1,100 do
for i=1,20000 do
Возникают вопросы, на которые не могу дать ответа.
Я в понедельник или во вторник опять на финдимидж насяду. Надо победить. Хочу попробовать перевести в массив, посмотреть результат, попробовать в 3 мерный сделать, думаю все-таки для теста попробовать на сях скомпилить - там по крайней мере точно будет понятен предел скорости. Это даст ответы имеет ли смысл дальше мучить луа и материться на него (думаю, что да) либо успокоится и искать решения именно в алгоритмизации.
Ты читаешь мои мысли. Я тоже хотел попробовать всё это сделать на Дельфине. Чтобы точно понимать все причины. Если честно, мне просто лень, поскольку меня то лично всё устраивает, я ничего не ищу при 80%. Но что-то мне подсказывает, что дело то именно в математике. Хочется всё-таки окончательно разобраться в этом вопросе, чтобы в будущем не наступать на те же грабли. Плюс там ещё вдобавок есть директивы компиляции, то же бы невредно посмотреть разницу. Не знаю, смогу ли себя заставить сесть за это дело.
Ну я себя в любом случае заставлю. Надо ставить точку и хотя бы в бета-релиз все выводить.
Я почему и начал грешить на математику, поскольку логика Кнайта мне абсолютно понятна. Он же сам об этом говорит, он начинает с самого уникального "цвета" (наиболее редкого) и так в порядке уменьшения уникальности, что и я всегда пытался делать. Только он это делает путём анализа картинки, а я сам - ручками. Увеличивая deviation, естественно уменьшается уникальность пикселей, увеличивается количество сравнений. В принципе то всё понятно. Так что обязательно надо посмотреть что там с математикой.
Процентовка нужна. Надо мне искать сотню изображений - я не буду сидеть и рисовать их неделю, мне сделать надо) Если так каждый раз подбирать, то это можно бросать все дела в жизни и только сидеть и рисовать их, резать. Помню был проект, так там было под 1к изображений. Это же сколько надо их обрабатывать потом?
Да почему рисовать, почему резать? Всё ищется без процентовки, у меня это параметр sim (similar - похожесть) . Лично я её использую только при поиске перекрытых друг другом картинок (мишеней например и там нет таких размеров, всё быстро поэтому) ну и иногда возможен вариант, когда тоже без similar не получается, если может быть расположен на картинке прицел (и тоже нет пропуска такого гигантского количества пикселей). Всё остальное ищется диапазоном, который я задаю 2-мя параметрами dev - абсолютное отклонение (требуется обычно для тёмных пикселей) и acc - процент отклонения цвета. Мой алгоритм как раз и позволяет автоматом задавать поиск. Надо мне искать только "красненькое", не обращая внимание на другие пиксели картинки. Я и формирую псевдокартинку условием r=100, R=255 или как в твоём примере задал фон r=195 и всех делов.
На самом деле всё чудесненько и быстренько ищется. Просто если и similar ускорить - это будет КОСМОС.
Лично я не знаю ни одного случая и даже не могу представить мысленно, когда меня бы что-то напрягло так, что это не ищется. Процентовка на малых картинках отрабатывает тоже на ура.
И наконец. Предположим, найдём и устраним тормоза. Что получим? Полсекунды поиска на твою картинку - это всё равно очень много. Ведь наверняка надо искать не одну только картинку. Так что запросто возможен вариант, когда для особо нагруженных случаев, просто придётся "рисовать" и "резать". Не сделаешь ты так, что ну прямо ищется абсолютно всё, да ещё и за десятую секунды.
И ещё один довод, тоже уже говорил про это. Иногда наоборот эта процентовка только мешает искать то, что надо. Тоже надо думать и понимать какой процент схожести задавать. 80% - это очень много. Пятая часть предполагается мусор - это что же за картинка такая???
Я не возражаю 90% для буковок - ты даже не заметишь потерь скорости.
И, как правило, буковки ты ведь не ищешь по всему фулХД.
Вопрос стоит конкретно - АЛГОРИТМ - найти причину, вот и всё. Это даст прирост, а он лишним не бывает.
И у тебя Дарк, всё-таки короткая память. Я ведь ещё в 19 году тебе говорил, в смысле спрашивал, а как ты игнорируя мусор (отсеивая какую-то часть пикселей) будешь отличать например "О" от "Q" или "1" от "l" . Пропуск мусора - это крайний случай, прибегать к которому надо абсолютно осознанно и понимая что должно искаться, а что нет. В большинстве случаев пропуск - это минус, так как это говорит лишь о том, что либо ищется не то что надо, либо что надо не ищется вовсе. Ну а в каких случаях требуется отсев уже устал повторять. У Кнайта изначально не было deviation и отсев нежелательных пикселей - это была просто необходимая данность. Потом всё было уже серьёзно переработано, но по умолчанию так остались 20% отсева (80% точности).
Главный минус у Кнайта, почему и используется так называемая точность, без которой частенько не ищется картинка - это то, что deviation - это процент отклонения. А какой процент отклонения задавать для пикселя с RGB (0,0,0)? У меня этот параметр - абсолютное значение (которое и нужно чтобы искались "тёмные" пиксели), процентовка идёт плюсом, и всё это в любых вариантах использования.
Я даже специальный скрипт делал, который мне показывал с какой гарантией ищется та или иная картинка, показывая допустимые граничные значения используемых мною параметров dev и acc в различных их сочетаниях. И я выбирал средние значения. Судя по моим экспериментам, запас варьирования всегда очень нехилый.
Дарк, наверное в скринилке, нужно всё-таки положительную высоту всегда прописывать - это типа норма. Наткнулся, что один старенький редактор не понимает файлы с отрицательной высотой. Короче, для себя принял именно общепринятый стандарт. Редактор - Paint Shop Pro 6 . Я им частенько пользуюсь. Установки не требует, простенький, не прихотливый.
Нет смысла изголяться. Перевёртывание происходит мгновенно, да и не только перевёртывание. Образ + скрин = молниеносно.
Дарк, у тебя там косячки в скринилке, забыл наверное, на автомате клепал - исправь. Они вроде ни на что не влияют, но чисто для порядка.
C.WriteFile(f, ffi.new("uint32_t[1]", h*w*3+54 )
Надо множить не на тройную ширину, а на длину строки, ну и далее тоже. Про то что там далее я не в теме (хотя все пишут по длине строки), но размер то файла точно врёт.
И мне как-то не понравилась запись в файл кусками. По древней, старпёрской привычке делать запись в файл одним оператором я сделал по-своему. Сначала сформировал единый заголовок, а потом допинываю требуемыми значениями.
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)
Парни, спасибо вам за моё обучение! Как хорошо, что надоумили плюнуть на память!
Надоела мне эта возня с типами данных и адресами. Сделал один глобальный СИ-массив, под разные случаи жизни, видимый всеми и везде и никаких проблем. Например нулевой массив содержит 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)