Что нового

Поиск пикселей в определённой области при неактивном окне

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
madmasles [?]
меньше действий в цикле
Да... прирост скорости в 23% - вот это оптимизация!
Только ещё бы 0 возвращала первым элементом, если ничего не найдено...
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
InnI [?]
Только ещё бы 0 возвращала первым элементом, если ничего не найдено...
Код:
;...
Local $aPixels[$iSize + 1][2] = [[0]]
;...
Еще немного оптимизировал, проверьте, пожалуйста, на скорость.


Добавлено:
Сообщение автоматически объединено:

blacklis [?]
мне с @error как-то спокойней
Можно добавить после циклов
Код:
If Not $aPixels[0][0] Then Return SetError(1)
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
madmasles [?]
Еще немного оптимизировал, проверьте, пожалуйста, на скорость.
Теперь ещё на 10% быстрее, чем предыдущая, и на 33% быстрее моей. Но!
При подстановке дескриптора окна функция ничего не находит. Дело в том, что, когда работаем с рабочим столом, то
Код:
DllStructGetData($tBits, 1, $iIndex)
возвращает 4294967295 (для белого цвета). А когда, например с блокнотом, то 16777215. Вероятно, как-то прозрачность замешана. Я это обходил через
Код:
BitAND(DllStructGetData($tBits, 1, $i), 0x00FFFFFF)
а в вашем коде не пойму, как убрать прозрачность.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
InnI,
Если поменять строку
Код:
;...
DllStructSetData($tByte, 2, 0xFF)
;...
на
Код:
;...
If Not $hWnd Then DllStructSetData($tByte, 2, 0xFF)
;...
то у меня (win7x86) работает.
И winXPx86 тоже.
Не работает так.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
InnI [?]
С блокнотом - да, с калькулятором - нет
Согласен. А так?
Код:
#include <Array.au3>
#include <WinAPIEx.au3>
#include <WindowsConstants.au3>

$hWin = WinGetHandle('[Class:CalcFrame]');0xE5EEF8
;~ $hWin = WinGetHandle('[Class:Notepad]');0xFFFFFF
;~ $hWin = 0;;0xFFFFFF
$iT = TimerInit()
$aPixels = _PixelGetArray(100, 100, 10, 10, 0x693977, $hWin)
ConsoleWrite(TimerDiff($iT) & @LF)
_ArrayDisplay($aPixels)

Func _PixelGetArray($i_X, $i_Y, $i_Width, $i_Height, $i_Color, $h_Wnd = 0, $f_FirstOnly = False)
	Local $i_Size = $i_Width * $i_Height, $a_Pixels[$i_Size + 1][2] = [[0]], $h_DC, $h_MemDC, $h_Bitmap, $t_Bits, $i_Index

	;If Not $h_Wnd Then $h_Wnd = _WinAPI_GetShellWindow()
	$h_DC = _WinAPI_GetDC($h_Wnd)
	$h_MemDC = _WinAPI_CreateCompatibleDC($h_DC)
	$h_Bitmap = _WinAPI_CreateCompatibleBitmap($h_DC, $i_Width, $i_Height)
	_WinAPI_SelectObject($h_MemDC, $h_Bitmap)
	_WinAPI_BitBlt($h_MemDC, 0, 0, $i_Width, $i_Height, $h_DC, $i_X, $i_Y, $SRCCOPY)
	_WinAPI_DeleteDC($h_MemDC)
	_WinAPI_ReleaseDC($h_Wnd, $h_DC)
	$t_Bits = DllStructCreate('dword[' & $i_Size & ']')
	$p_Bits = DllStructGetPtr($t_Bits)
	_WinAPI_GetBitmapBits($h_Bitmap, 4 * $i_Size, $p_Bits)
	_WinAPI_DeleteObject($h_Bitmap)
	For $i = 0 To $i_Height - 1
		For $j = 0 To $i_Width - 1
			$i_Index += 1
			If BitAND(DllStructGetData($t_Bits, 1, $i_Index), 0x00FFFFFF) = $i_Color Then
				$a_Pixels[0][0] += 1
				$a_Pixels[$a_Pixels[0][0]][0] = $j + $i_X
				$a_Pixels[$a_Pixels[0][0]][1] = $i + $i_Y
				If $f_FirstOnly Then ExitLoop 2
			EndIf
		Next
	Next
	ReDim $a_Pixels[$a_Pixels[0][0] + 1][2]
	Return $a_Pixels
EndFunc   ;==>_PixelGetArray
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
madmasles [?]
Калькулятор. Вид - Инженерный. Моя функция находит два пикселя
Код:
$test = _PixelGetArray(100, 100, 10, 10, 0x693977, WinGetHandle("Калькулятор")) ; 102:107, 102:108

Ваша функция
Код:
$hWin = WinGetHandle('[Class:CalcFrame]')
$aPixels = _PixelGetArray(100, 100, 10, 10, 0x693977, $hWin) ; НЕ находит
$aPixels = _PixelGetArray( 90,  90, 20, 20, 0x693977, $hWin) ; НЕ находит
$aPixels = _PixelGetArray( 80,  80, 30, 30, 0x693977, $hWin) ; находит 102:107, 102:108
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
InnI,
Хотел убрать BitAND в цикле. Не получилось пока. Все свои сообщения поправил.
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
madmasles [?]
Все свои сообщения поправил
Только я для себя уберу строку
Код:
If Not $h_Wnd Then $h_Wnd = _WinAPI_GetShellWindow()
Иначе поиск происходит на самом рабочем столе ("на обоях"), а мне нужно как бы "на скриншоте", т.е. относительно экрана.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
InnI,
Я бы еще добавил проверку, что если окно есть, то оно не свернуто. Типа
Код:
;...
	If $h_Wnd Then
		If BitAND(WinGetState($h_Wnd), 16) Then
			$a_Pixels[0][0] = -1
			Return $a_Pixels
		EndIf
	EndIf
	;...



Добавлено:
Сообщение автоматически объединено:

InnI [?]
Иначе поиск происходит на самом рабочем столе ("на обоях"), а мне нужно как бы "на скриншоте", т.е. относительно экрана.
Логично.
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
madmasles [?]
проверку, что если окно есть, то оно не свернуто
Тогда уж и на то, что окно не скрыто. Вдруг оно в трэй "свёрнуто"...


Добавлено:
Сообщение автоматически объединено:

madmasles
У вас два цикла, поэтому
Код:
If $f_FirstOnly Then ExitLoop 2
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
Переписал _PixelGetArray() из ответа #19 с использованием строковых функций.
Время поиска массива пикселей на большой картинке значительно сократилось.
Например, поиск четырёх пикселей по углам FullHD скриншота:
- без допуска цвета: было 3650 мс, стало 200 мс
- с допуском цвета: было 7000 мс, стало 4300 мс
Но эта функция может проигрывать в скорости предыдущей в случае поиска только первого совпадения при нахождении пикселя в верхней части картинки.
Также, добавил в элемент $aArray[0][1] возвращаемого массива время поиска в миллисекундах (раньше этот элемент не использовался)
Код:
#include <WinAPIGdi.au3>

$aPixels = _PixelGetArray2(0, 0, 100, 100, 0x000000, 10)

#include <Array.au3>
_ArrayDisplay($aPixels)

; -----------------------------------------------------------------------------------------------------

Func _PixelGetArray2($iX, $iY, $iWidth, $iHeight, $iColor, $iShade = 0, $hWnd = 0, $fFirstOnly = False)
  Local $iTime = TimerInit()
  Local $iSize = $iWidth * $iHeight, $aPixels[$iSize + 1][2] = [[0]]
  Local $hDC = _WinAPI_GetDC($hWnd)
  Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
  Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iWidth, $iHeight)
  _WinAPI_SelectObject($hMemDC, $hBitmap)
  _WinAPI_BitBlt($hMemDC, 0, 0, $iWidth, $iHeight, $hDC, $iX, $iY, 0x00CC0020) ; $SRCCOPY
  _WinAPI_DeleteDC($hMemDC)
  _WinAPI_ReleaseDC($hWnd, $hDC)
  Local $tBits = DllStructCreate('byte[' & $iSize * 4 & ']')
  _WinAPI_GetBitmapBits($hBitmap, $iSize * 4, DllStructGetPtr($tBits))
  _WinAPI_DeleteObject($hBitmap)
  Local $sText = StringTrimLeft(DllStructGetData($tBits, 1), 2)
  Local $iStep = $iWidth * 8
  Local $aLines[$iHeight], $c = 0
  For $i = 1 To StringLen($sText) Step $iStep
    $aLines[$c] = StringMid($sText, $i, $iStep)
    $c += 1
  Next
  Local $iPos, $iStart, $iLen = StringLen($aLines[0])
  $iColor = StringRegExpReplace(Hex($iColor, 6), "(.{2})(.{2})(.{2})", "$3$2$1")
  If $iShade Then
    Local $iB = Dec(StringMid($iColor, 1, 2))
    Local $iG = Dec(StringMid($iColor, 3, 2))
    Local $iR = Dec(StringMid($iColor, 5, 2))
    For $i = 0 To $iHeight - 1
      For $j = 1 To $iLen Step 8
        Switch Dec(StringMid($aLines[$i], $j, 2))
          Case $iB - $iShade To $iB + $iShade
            Switch Dec(StringMid($aLines[$i], $j + 2, 2))
              Case $iG - $iShade To $iG + $iShade
                Switch Dec(StringMid($aLines[$i], $j + 4, 2))
                  Case $iR - $iShade To $iR + $iShade
                    $aPixels[0][0] += 1
                    $aPixels[$aPixels[0][0]][0] = ($j - 1) / 8 + $iX
                    $aPixels[$aPixels[0][0]][1] = $i + $iY
                    If $fFirstOnly Then ExitLoop 2
                EndSwitch
            EndSwitch
        EndSwitch
      Next
    Next
  Else
    For $i = 0 To $iHeight - 1
      $iStart = 1
      While $iStart <= $iLen
        $iPos = StringInStr($aLines[$i], $iColor, 1, 1, $iStart)
        If $iPos Then
          If Mod($iPos - 1, 8) Then
            $iStart = $iPos + 2
            ContinueLoop
          EndIf
          $aPixels[0][0] += 1
          $aPixels[$aPixels[0][0]][0] = ($iPos - 1) / 8 + $iX
          $aPixels[$aPixels[0][0]][1] = $i + $iY
          If $fFirstOnly Then ExitLoop 2
          $iStart = $iPos + 8
        Else
          ExitLoop
        EndIf
      WEnd
    Next
  EndIf
  ReDim $aPixels[$aPixels[0][0] + 1][2]
  $aPixels[0][1] = Round(TimerDiff($iTime))
  Return $aPixels
EndFunc  ;==>_PixelGetArray2
 

eralex

Знающий
Сообщения
64
Репутация
5
Зачем велосипед изобретать? Есть же библиотека ImageSearchHWND.dll
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
eralex [?]
Или всё-таки ImageSearchDLL.dll ? Так вроде она картинки ищет, а не массивы пикселей...

Есть же С++. Зачем надо было AutoIt изобретать? ;)
 

eralex

Знающий
Сообщения
64
Репутация
5
Именно ImageSearchHWND.dll - версия этой библиотеки для поиска в неактивных(скрытых) окнах. Пользуюсь много лет её при написании ботов.
И ищет картинки - да, не массив пикселей. Но массив пикселей можно в виде картинки же сохранить и подсунуть либе для поиска.
 

InnI

AutoIT Гуру
Сообщения
4,951
Репутация
1,446
eralex [?]
массив пикселей можно в виде картинки же сохранить
Чтобы сохранить массив пикселей в виде картинки, нужно этот самый массив получить. Вот именно это функция и делает.

Да и смысл обновления функции был в использовании другого подхода - ускорении поиска за счёт чтения бинарных данных через функции работы со строками, как с наиболее быстрыми в AutoIt. Здесь есть люди, кому больше интересно исследовать возможности языка, чем пользоваться готовым результатом. И я - один из них.
Кстати, вот очередной велосипед по вашему профилю: PicInPic :smile:
 

eralex

Знающий
Сообщения
64
Репутация
5
Только ImageSearchHWND.au3 я переписал под свои нужды.
 

Вложения

  • ImageSearchHWND.rar
    37.6 КБ · Просмотры: 26
Верх