Что нового

Функция _PixelSearchEx() для поиска пикселя по цвету из Bitmap

Yura

Знающий
Сообщения
36
Репутация
7
Сразу скажу - вопрос не об UDF PixelSearchEx. Искал максимально быстрый способ найти пиксель по цвету из памяти после того, как сделан скриншот. Нашел на англоязычном форуме такой же вопрос в 2012 году и весьма любопытный скрипт от Yashied в ответ. Навожу полный текст ответа: "If do not take into account the time for creating a screenshot, the _PixelSearchEx() is approximately 4 times faster than native PixelSearch(). To run this example you will need to download a WinAPIex UDF library."
Код:
#Include <ScreenCapture.au3>
#Include <WinAPIEx.au3>

$Timer = TimerInit()
$Pos = PixelSearch(0, 0, @DesktopWidth, @DesktopHeight, 0x00FFFF)
ConsoleWrite('PixelSearch()   => X = ' & $Pos[0] & ', Y = ' & $Pos[1] & ', Time = ' & TimerDiff($Timer) & @CR & @CR)

$hBitmap = _ScreenCapture_Capture()

$Timer = TimerInit()
$Pos = _PixelSearchEx($hBitmap, 0x00FFFF)
ConsoleWrite('PixelSearchEx() => X = ' & $Pos[0] & ', Y = ' & $Pos[1] & ', Time = ' & TimerDiff($Timer) & @CR)

Func _PixelSearchEx($hBitmap, $iColor)

    Static $pProc = 0

    If Not $pProc Then
        If @AutoItX64 Then

            ; Not implemented!

            Return SetError(1, 0, 0)
        Else
            $pProc = __Init(Binary( _
                      '0x5553575631C0505050837C24280074118B6C2428837D14007407B801000000EB' & _
                        '0231C021C0747F8B6C24288B5D084B891C2431C03B04247F6DC7442404000000' & _
                        '008B6C24288B5D044B3B5C24047C528B6C24288B5D148B7C24048B34248B6C24' & _
                        '280FAF750401F7C1E70201FB895C24088B6C24088B5D0081E3FFFFFF003B5C24' & _
                        '2475188B5C24048B6C24288B7D082B3C244FC1E71009FB89D8EB14FF44240471' & _
                        'A0FF0C24718CB8FFFFFFFFEB0231C083C40C5E5F5B5DC21000'))
        EndIf
    EndIf

    Local $tDIB, $tInt, $tPos, $aPos, $Ret, $Error = True

    $hBitmap = _WinAPI_CopyBitmap($hBitmap)
    If @error Then
        Return SetError(1, 0, 0)
    EndIf
    Do
        $tDIB = DllStructCreate($tagDIBSECTION)
        If (Not _WinAPI_GetObject($hBitmap, DllStructGetSize($tDIB), DllStructGetPtr($tDIB))) Or (DllStructGetData($tDIB, 'bmBitsPixel') <> 32) Or (DllStructGetData($tDIB, 'biCompression')) Then
            ExitLoop
        EndIf
        $Ret = _WinAPI_CallWindowProc($pProc, 0, $iColor, DllStructGetPtr($tDIB), 0)
        If (@error) Or ($Ret = -1) Then
            ExitLoop
        EndIf
        $Error = False
    Until 1
    _WinAPI_DeleteObject($hBitmap)
    If $Error Then
        Return SetError(1, 0, 0)
    EndIf
    $tInt = DllStructCreate('int')
    $tPos = DllStructCreate('ushort;ushort', DllStructGetPtr($tInt))
    DllStructSetData($tInt, 1, $Ret)
    Dim $aPos[2]
    For $i = 0 To 1
        $aPos[$i] = DllStructGetData($tPos, $i + 1)
    Next
    Return $aPos
EndFunc   ;==>_PixelSearchEx

Попробовал у себя. Закомментировал строки "$hBitmap = _WinAPI_CopyBitmap($hBitmap)" и "_WinAPI_DeleteObject($hBitmap)"- скорость выросла еще в 3-4 раза и уже стала такая же как в UDF FastFind в функции FFNearestPixel (2-3 мс у меня на картинке размером в экран 17 дюймов). Не знаю зачем здесь клонируется Bitmap и работа идет с клоном, может он как-то портится в ходе работы. Возможно можно вынести часть функции в основную программу, если я хочу много раз использовать функцию на одной и той же Bitmap. Очень хотелось бы иметь возможность находить не 1 пиксель за 1 вызов функции, а сразу столько, сколько надо.
Что и как сделать я не знаю просто потому, что не могу разобрать код. Споткнулся сразу на "__Init(Binary( _.......". Что это, откуда и зачем это? Догадываюсь, что это какой-то набор команд, как понять что там зашифровано... и что за __Init. Пожалуйста, помогите разобрать по винтикам функцию _PixelSearchEx(). Я думаю, что это пригодится не только мне, а еще очень многим.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
_WinAPI_CopyBitmap() и _WinAPI_DeleteObject() никак не могут повлиять на скорость, т.к. вызываются только один раз. А вот то, что находится внутри __Init(), это и есть функция поиска пиксела, реализованная на машинном коде.
 
Автор
Y

Yura

Знающий
Сообщения
36
Репутация
7
Yashied сказал(а):
_WinAPI_CopyBitmap() и _WinAPI_DeleteObject() никак не могут повлиять на скорость, т.к. вызываются только один раз. А вот то, что находится внутри __Init(), это и есть функция поиска пиксела, реализованная на машинном коде.

Что значит не могут повлиять на скорость? 1 раз тоже время занимает. Вот без закомментированных строк (для сравнения брал FFNearestPixel(), сравнение на той же картинке, просто в FFNearestPixel() привязка к другим координатам):

FFNearestPixel() => X = 903, Y = 53, Time = 2.1803790826262
PixelSearchEx() => X = 886, Y = 238, Time = 7.0014538334931

Вот с закомментированными _WinAPI_CopyBitmap():
FFNearestPixel() => X = 903, Y = 53, Time = 2.0580277298074
PixelSearchEx() => X = 886, Y = 238, Time = 2.06677650105877

Может подскажете как этот код достать? Дизассемблировать? Или может поделитесь исходником :smile:
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Yura
быстрый способ найти пиксель по цвету из памяти после того, как сделан скриншот
Это умеет DLL FindPixel - поиск пикселей. Рабочая версия 1.3 прикреплена к ответу #10.

возможность находить не 1 пиксель за 1 вызов функции, а сразу столько, сколько надо
Если не "сколько надо", а "сколько есть", то посмотрите _PixelSearchArray (ответ #9) на базе этой же DLL.
 
Автор
Y

Yura

Знающий
Сообщения
36
Репутация
7
InnI сказал(а):
Это умеет DLL FindPixel - поиск пикселей. Рабочая версия 1.3 прикреплена к ответу #10.
Спасибо, посмотрел, но есть такой вопрос. Я так понял, что если у меня уже есть готовый Bitmap, то я не смогу положить его в буфер, чтоб FindPixel искал в нем? Надо еще раз делать скриншот через ScreenToBuff()?
Я хочу отойти от использования UDF FastFind, потому что для работы с ней надо делать скриншоты ее же функцией FFSnapShot. Я уже делаю скрины и вырезаю с них что мне надо (в данном случае контрол) так:
Код:
Func _WinCapture($hWnd, $iWidth, $iHeight)
    Local $iH, $iW, $hDDC, $hCDC, $hBMP
    $hDDC = _WinAPI_GetDC($hWnd)
	;MsgBox(0,"",$hDDC)
    $hCDC = _WinAPI_CreateCompatibleDC($hDDC)
    $hBMP = _WinAPI_CreateCompatibleBitmap($hDDC, $iWidth, $iHeight)
    _WinAPI_SelectObject($hCDC, $hBMP)
	_WinAPI_PrintWindow ($hWnd, $hCDC)
    _WinAPI_ReleaseDC($hWnd, $hDDC)
    _WinAPI_DeleteDC($hCDC)
    ;==============crop=============
	$hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBMP)
	_WinAPI_DeleteObject($hBMP)
	$hBitmap_clone = _GDIPlus_BitmapCloneArea($hBitmap, $Control_in_win_pos[0], $Control_in_win_pos[1], $control_pos[2]-10, $control_pos[3]-10, $GDIP_PXF24RGB)
;_GDIPlus_ImageSaveToFile($hBitmap_clone, "!.bmp")
	_GDIPlus_BitmapDispose($hBitmap)
	$hBMP_Control = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap_clone)
	_GDIPlus_BitmapDispose($hBitmap_clone)
	Return $hBMP_Control
EndFunc   ;==>_WinCapture

и потом $hBMP_Control использую в UDF ImageSearch для поиска картинок в перекрытых и задвинутых за границы экрана окнах. Не хочется дважды скриншотить то же самое для ImageSearch и FastFind. Кроме того хочу вывести GUI окошко, в котором будет показывать уменьшенное изображение того, что делается в перекрытом окне (скриншоты повесить через AdlibRegister). Имея $hBMP_Control это не сложно, поэтому использовать полученную через _WinAPI_PrintWindow() битовую карту предпочтительней.
Если FindPixel можно "скормить" мою $hBMP_Control, то тогда было бы супер.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Yura
готовый Bitmap <...> положить его в буфер
Прочитайте ответ #2 в теме DLL. Там есть функция ImageLoadMem(Mem, Size) Функция загружает в буфер BMP картинку из памяти. Пробуйте.
 
Автор
Y

Yura

Знающий
Сообщения
36
Репутация
7
InnI сказал(а):
Yura
Прочитайте ответ #2 в теме DLL. Там есть функция ImageLoadMem(Mem, Size) Функция загружает в буфер BMP картинку из памяти. Пробуйте.
Спасибо за помощь. Дальше разговор переместился в тему о FindPixel :smile:
 
Верх