Что нового

Альтернатива PixelGetColor

SyDr

Сидра
Сообщения
651
Репутация
158
Как известно, у некоторых (например, у меня) функция PixelGetColor работает чрезвычайно медленно при включённой композиции рабочего стола (на получение 500-1000 цветов пикселов уходит с десяток секунд).
Никто не подскажет альтернативу? Не получается как-то найти.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Делаешь скриншот необходимой области в память и далее работаешь с битмапом.
 

Belfigor

Модератор
Локальный модератор
Сообщения
3,608
Репутация
941
Очень кашерная штука. Скорость выполнения отличается от стандартного пкг в несколько тысяч раз. Работает через dgi32.dll
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Я бы рекомендовал делать скриншот с помощью следующей функции (особенно в XP):

Код:
Func _ScreenToBitmap()

    Local $Timer, $hBitmap = 0

    ClipPut('')
    Send('{PRINTSCREEN}')
    $Timer = TimerInit()
    While TimerDiff($Timer) < 1000
        _ClipBoard_Open(0)
        $hBitmap = _ClipBoard_GetDataEx($CF_BITMAP)
        _ClipBoard_Close()
        If $hBitmap Then
            ExitLoop
        EndIf
        Sleep(10)
    WEnd
	Return $hBitmap
EndFunc   ;==>_ScreenToBitmap
 
Автор
SyDr

SyDr

Сидра
Сообщения
651
Репутация
158
Ммммм... Ну ладно...
С помощью Prt Sc делать скрин не очень надёжно, если установлен софт для снятия скриншотов с экрана.

А если мне нужно сделать скриншот клиентской части окна? Как узнать её координаты? Можно, конечно, писать костыли вида +(-)8, но это, имхо, некрасиво.

Можно и другого вида костыль - перетащить курсор мыши в (0, 0) клиентской части окна, узнать при этом глобальные координаты курсора, перетащить его обратно...

Но всё же, есть что-нибудь нормальное?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Делаешь битмап всего экрана. Определяешь координаты нужного окна с помощью WinGetPos() и пошло-поехало. Я бы использовал GDI+, с ним проще и понятнее работать. Поиграйся с этим, я не вижу здесь особых проблем.
 
Автор
SyDr

SyDr

Сидра
Сообщения
651
Репутация
158
В том то и дело, что WinGetPos и WinGetClientSize недостаточно.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Вот так понятно будет?

Код:
#Include <Constants.au3>
#Include <WinAPIEx.au3>
#Include <WindowsConstants.au3>

Run(@ProgramFilesDir & '\Microsoft Games\Minesweeper\MineSweeper.exe')
$hWnd = WinWait('Minesweeper', '', 3)
If Not $hWnd Then
	Exit
EndIf

; Чтобы мины успели нарисоваться!
Sleep(1000)

$Size = WinGetClientSize($hWnd)
$hDC = _WinAPI_GetDC($hWnd)
$hMemDC = _WinAPI_CreateCompatibleDC($hDC)
$hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $Size[0], $Size[1])
$hSv = _WinAPI_SelectObject($hMemDC, $hBitmap)
_WinAPI_BitBlt($hMemDC, 0, 0, $Size[0], $Size[1], $hDC, 0, 0, $SRCCOPY)
_WinAPI_SelectObject($hMemDC, $hSv)
_WinAPI_DeleteDC($hMemDC)
_WinAPI_ReleaseDC($hWnd, $hDC)

; Здесь у тебя есть готовый битмап ($hBitmap), делай с ним что хочешь!

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

; Демонстрация
$hWnd = GUICreate('', $Size[0], $Size[1], 50, 50, -1, $WS_EX_TOPMOST)
GUISetState()
$hDC = _WinAPI_GetDC($hWnd)
$L = $Size[0] * $Size[1]
$tBits = DllStructCreate('dword[' & $L & ']')
_WinAPI_GetBitmapBits($hBitmap, 4 * $L, DllStructGetPtr($tBits))
$Offset = 1
For $y = 0 To $Size[1] - 1
	For $x = 0 To $Size[0] - 1
		_WinAPI_SetPixel($hDC, $x, $y, DllStructGetData($tBits, 1, $Offset))
		$Offset += 1
	Next
Next
_WinAPI_ReleaseDC($hWnd, $hDC)

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

; Ищем точку с цветом 0x87BEF5 (RGB)
$ARGB = _WinAPI_IntToDWord(BitOR(0x87BEF5, 0xFF000000))
$L = $Size[0] * $Size[1]
$tBits = DllStructCreate('dword[' & $L & ']')
_WinAPI_GetBitmapBits($hBitmap, 4 * $L, DllStructGetPtr($tBits))
$Timer = TimerInit()
$Offset = -1
For $i = 1 To $L
	If DllStructGetData($tBits, 1, $i) = $ARGB Then
		$Offset = $i
		ExitLoop
	EndIf
Next
ConsoleWrite('API' & @CR)
ConsoleWrite(TimerDiff($Timer) & @CR)
If $Offset = -1 Then
	ConsoleWrite('Not Found!' & @CR)
Else
	ConsoleWrite('X = ' & Mod($Offset, $Size[0]) - 1 & @CR)
	ConsoleWrite('Y = ' & Ceiling($Offset / $Size[0]) - 1 & @CR)
EndIf

ConsoleWrite('' & @CR)
;----------------------------------------

Opt('PixelCoordMode', 2)
$Timer = TimerInit()
$Offset = False
For $y = 0 To $Size[1] - 1
	For $x = 0 To $Size[0] - 1
		If PixelGetColor($x, $y, $hWnd) = 0x87BEF5 Then
			$Offset = True
			ExitLoop 2
		EndIf
	Next
Next
ConsoleWrite('AutoIt' & @CR)
ConsoleWrite(TimerDiff($Timer) & @CR)
If Not $Offset Then
	ConsoleWrite('Not Found!' & @CR)
Else
	ConsoleWrite('X = ' & $x & @CR)
	ConsoleWrite('Y = ' & $y & @CR)
EndIf

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

Do
Until GUIGetMsg() = -3


API быстрее PixelGetColor() в 7.5 тысяч (!) раз. (Windows 7 + Aero)
 

NoMad73rus

Продвинутый
Сообщения
124
Репутация
80
Belfigor сказал(а):
Очень кашерная штука. Скорость выполнения отличается от стандартного пкг в несколько тысяч раз. Работает через dgi32.dll
Стоит убрать ToolTip из цыкла с PGC и :
Код:
Starting test in field (0,0) to (100,100)
Native: 114.489106916003
UDF   : 294.745804953388

Yashied сказал(а):
Вот так понятно будет?
Большое спасибо за такое наглядное посвящение в работу с Api.
Начинаю убирать из кода грабли :thumbs_up:
 

edward_freedom

Осваивающий
Сообщения
200
Репутация
44
Yashied
Спасибо большое за скрипт!! :laugh:Ты просто гений! :beer:
 

AlexxxRu

Знающий
Сообщения
19
Репутация
6
Как из aRGB перевести в RGB?
Т.к. могут бы прозрачные элементы, и при PixelGetColor они не учитываются. Соответственно, я думаю, из-за этого цвета и не находятся.
Например:
aRGB = 0x32FFFFFF
И я беру пиксель из этой области. И он получается:
RGB = 0x00FFFFFF
Вот и хочу наоборот, чтобы избавится от прозрачности(первых 2х байт aRGB).
Я чуть-чуть код изменил:
Код:
; Ищем точку с цветом 0x87BEF5 (RGB)
$Color ='0x'&Hex(PixelGetColor(421,319))

Координаты PixelGetColor в середине клиента, к-й я проверяю. И весь код перебит под этот клиент. Везде константа 0x87BEF5 была заменена на $color
 

Ilyialat

Новичок
Сообщения
54
Репутация
2
Всем привет =)
Код:
Func Bitmap()
Local $hDC = _WinAPI_GetDC($hWnd) 
Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC) 
Local $hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $Size[0], $Size[1])
Local $hSv = _WinAPI_SelectObject($hMemDC, $hBitmap)
_WinAPI_BitBlt($hMemDC, 0, 0, $Size[0], $Size[1], $hDC, 0, 0, $SRCCOPY)
_WinAPI_SelectObject($hMemDC, $hSv)
_WinAPI_DeleteDC($hMemDC)
_WinAPI_ReleaseDC($hWnd, $hDC)
Global $tBits = DllStructCreate('dword[' & $L & ']')
_WinAPI_GetBitmapBits($hBitmap, 4 * $L, DllStructGetPtr($tBits))
_WinAPI_DeleteObject($hBitmap)
_WinAPI_DeleteObject($hMemDC)
_WinAPI_DeleteObject($hSv)
EndFunc



Func SearchAndWork() 
Bitmap()
For $fx=$x To $x1 Step 1
For $fy=$y to $y1 Step 1
For $c=0 to UBound($Search)-1
Local $iPixelColor = Mod(DllStructGetData($tBits, 1, $fY * $Size[0] + $fX), 0x1000000)
if ($iPixelColor == $search[$c]) Then
Work($fx,$fy)
Bitmap()
EndIf	
Next
Next
Next
EndFunc
У меня область экрана маленькая
$x=290
$y=239
$x1=1041
$y1=544
но её сканирует около 6 сек. Что я не так сделал? Нагрузка на процессор большая
Исключение Mod на скорость не влияет...
ещё - чем меньше в массиве search элементов. тем быстрее работает...
6 сек сканирует 1 элемент...
Помогите пожалуйста)
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Ilyialat,
Предупреждение За нарушение правил форума (пункт В.11):
Любые отрывки AutoIt кода необходимо заключать в тег [autoit]
autoit.gif
(подробнее), а обычный код соответственно в тег [code]
code.gif
(подробнее). Также большие выдержки текста помещайте под тег [spoiler]
spoiler.gif
(подробнее), там где это поддерживается естественно. Как в случае с названием темы, также короткое и эргономичное сообщение привлекает больше внимания, и шансы на получение конкретного ответа увеличиваются.


С уважением, ваш Модератор.




Ваше последнее сообщение удалил, предыдущее поправил так, как должно быть. Постарайтесь соблюдать наши правила.
 
G

GenoTIP

Гость
я вот так проверяю пиксели на цвет в прямоугольнике[x][5] в ширину 5 px, в высоту неограниченно, по времени в среднем ~500мс, идея в том, что не надо проверять на цвет каждую строку(если изначально знаешь что область вся одного цвета, помогает узнать размер)
Код:
Func square_arr_pixel($color)
    Local $i,$iReDim,$iCoord
    $i = 1
    Dim $square_arr_pixel[$i][5]
    _ArrayTrim( $square_arr_pixel, 1, 1,1)
    $coord = PixelSearch( $x,$y,$x2,$y2, '0x'&$color )   ; 2
    ;_ArrayDisplay($coord)
    If Not @error Then
        $coordY = $coord[1]
        $BottomPixel = Hex(PixelGetColor( $coord[0] , $coord[1]),6)
        While $BottomPixel == $color 
            $BottomPixel = Hex(PixelGetColor( $coord[0] , $coordY),6)
            If $BottomPixel == $color Then
                $coordY += 21
            Else  
                $BottomPixelNew = Hex(PixelGetColor( $coord[0] , $coordY),6)
                While $BottomPixelNew <> $color 
                    $BottomPixelNew = Hex(PixelGetColor( $coord[0] , $coordY),6)
                    If $BottomPixelNew == $color Then
                        $Find = $coordY-$coord[1]+2
                        ReDim $square_arr_pixel[$Find][5]
                        ;_ArrayDisplay($square_arr_pixel)
                    EndIF
                   $coordY -= 1
                WEnd
            EndIf
        WEnd
    EndIf
    
    Return $square_arr_pixel
EndFunc
 

Ilyialat

Новичок
Сообщения
54
Репутация
2
Ну это кажется медленнее будет работать чем через winapi :-\
Что, никто не поможет? Может у меня комп слабый просто?
 
G

GenoTIP

Гость
идеальный вариант
Код:
Func square_arr_pixel($color)
    Local $i,$iReDim,$iCoord
    $i = 1

    Dim $square_arr_pixel[$i][5]
    _ArrayTrim( $square_arr_pixel, 1, 1,1)
    $coord = PixelSearch( $x,$y,$x2,$y2, '0x'&$color )
    If Not @error Then
        $coordY = $coord[1]
        $BottomPixel =_PixelGetColorEx ( $coord[0] , $coord[1],$hWnd2 )
        $ww = TimerInit()
        While $BottomPixel == $color ;And ($end <> 1)
            $BottomPixel =_PixelGetColorEx ( $coord[0] , $coordY, $hWnd2 )
            If $BottomPixel == $color Then
                $coordY += 21
            Else
                $BottomPixelNew =_PixelGetColorEx ( $coord[0] , $coordY, $hWnd2 )
                While $BottomPixelNew <> $color
                    $BottomPixelNew =_PixelGetColorEx ( $coord[0] , $coordY,$hWnd2 )
                    If $BottomPixelNew == $color Then
                        $Find = $coordY-$coord[1]+2
						;ConsoleWrite($Find)
                        ReDim $square_arr_pixel[$Find][5]
						;$end = 1
                    EndIF
                   $coordY -= 1
                WEnd
            EndIf
        WEnd
        $www = StringTrimRight ( TimerDiff($ww),10)
        _GUICtrlStatusBar_SetText($StatusBar1, "Картинка обработана за: "&$www , 0)
    EndIf
    Return $square_arr_pixel
EndFunc

Func _PixelGetColorEx ($x, $y, $hWnd)
    Local $hDDC, $hCDC, $hBMP
    $Wkoord = WinGetPos( $hWnd )                ; чтоб координаты стали относительные
	$WClient = WinGetClientSize( $hWnd  )                ; клиентская часть окна
    $iWidth = _WinAPI_GetWindowWidth($hWnd)
    $iHeight = _WinAPI_GetWindowHeight($hWnd)
    $hDDC = _WinAPI_GetDC($hWnd)
    $hCDC = _WinAPI_CreateCompatibleDC($hDDC)
    $hBMP = _WinAPI_CreateCompatibleBitmap($hDDC, $iWidth, $iHeight) ; создаем битовый массив
    _WinAPI_SelectObject($hCDC, $hBMP)
    DllCall("User32.dll", "int", "PrintWindow", "hwnd", $hWnd, "hwnd", $hCDC, "int", 0)
    ;_WinAPI_SaveHBITMAPToFile( @ScriptDir & '\MyImage.bmp', $hBMP)
	$iBorder = ($Wkoord[2] - $WClient[0]) / 2 ;получаем ширину бордюра
    $iCaption = ($Wkoord[3] - $WClient[1]) - $iBorder ;получаем высоту заголовка окна
	$iX = $x  + $iBorder       ; пересчет на относительные координаты
    $iY = $y  + $iCaption      ; с учетом бордюра(4?) и титульной строки (23?)
    $c = _WinAPI_GetPixel($hCDC, $iX, $iY)
    $R = _WinAPI_GetRValue($c)
    $G = _WinAPI_GetGValue($c)
    $B = _WinAPI_GetBValue($c)
    _WinAPI_ReleaseDC($hWnd, $hDDC)
    _WinAPI_DeleteDC($hCDC)
    _WinAPI_DeleteObject($hBMP) ; очищаем битовый массив
    Return Hex(_WinAPI_RGB($R, $G, $B), 6)
EndFunc   ;==>_PixelGetColorEx
 

nowost

Знающий
Сообщения
178
Репутация
17
можно пару вопросов по :
Код:
#Include <Constants.au3>
#Include <WinAPIEx.au3>
#Include <WindowsConstants.au3>

Run(@ProgramFilesDir & '\Microsoft Games\Minesweeper\MineSweeper.exe')
$hWnd = WinWait('Minesweeper', '', 3)
If Not $hWnd Then
    Exit
EndIf

; Чтобы мины успели нарисоваться!
Sleep(1000)

$Size = WinGetClientSize($hWnd)
$hDC = _WinAPI_GetDC($hWnd)
$hMemDC = _WinAPI_CreateCompatibleDC($hDC)
$hBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $Size[0], $Size[1])
$hSv = _WinAPI_SelectObject($hMemDC, $hBitmap)
_WinAPI_BitBlt($hMemDC, 0, 0, $Size[0], $Size[1], $hDC, 0, 0, $SRCCOPY)
_WinAPI_SelectObject($hMemDC, $hSv)
_WinAPI_DeleteDC($hMemDC)
_WinAPI_ReleaseDC($hWnd, $hDC)

; Здесь у тебя есть готовый битмап ($hBitmap), делай с ним что хочешь!

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

; Демонстрация
$hWnd = GUICreate('', $Size[0], $Size[1], 50, 50, -1, $WS_EX_TOPMOST)
GUISetState()
$hDC = _WinAPI_GetDC($hWnd)
$L = $Size[0] * $Size[1]
$tBits = DllStructCreate('dword[' & $L & ']')
_WinAPI_GetBitmapBits($hBitmap, 4 * $L, DllStructGetPtr($tBits))
$Offset = 1
For $y = 0 To $Size[1] - 1
    For $x = 0 To $Size[0] - 1
        _WinAPI_SetPixel($hDC, $x, $y, DllStructGetData($tBits, 1, $Offset))
        $Offset += 1
    Next
Next
_WinAPI_ReleaseDC($hWnd, $hDC)

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

; Ищем точку с цветом 0x87BEF5 (RGB)
$ARGB = _WinAPI_IntToDWord(BitOR(0x87BEF5, 0xFF000000))
$L = $Size[0] * $Size[1]
$tBits = DllStructCreate('dword[' & $L & ']')
_WinAPI_GetBitmapBits($hBitmap, 4 * $L, DllStructGetPtr($tBits))
$Timer = TimerInit()
$Offset = -1
For $i = 1 To $L
    If DllStructGetData($tBits, 1, $i) = $ARGB Then
        $Offset = $i
        ExitLoop
    EndIf
Next
ConsoleWrite('API' & @CR)
ConsoleWrite(TimerDiff($Timer) & @CR)
If $Offset = -1 Then
    ConsoleWrite('Not Found!' & @CR)
Else
    ConsoleWrite('X = ' & Mod($Offset, $Size[0]) - 1 & @CR)
    ConsoleWrite('Y = ' & Ceiling($Offset / $Size[0]) - 1 & @CR)
EndIf

ConsoleWrite('' & @CR)
;----------------------------------------

Opt('PixelCoordMode', 2)
$Timer = TimerInit()
$Offset = False
For $y = 0 To $Size[1] - 1
    For $x = 0 To $Size[0] - 1
        If PixelGetColor($x, $y, $hWnd) = 0x87BEF5 Then
            $Offset = True
            ExitLoop 2
        EndIf
    Next
Next
ConsoleWrite('AutoIt' & @CR)
ConsoleWrite(TimerDiff($Timer) & @CR)
If Not $Offset Then
    ConsoleWrite('Not Found!' & @CR)
Else
    ConsoleWrite('X = ' & $x & @CR)
    ConsoleWrite('Y = ' & $y & @CR)
EndIf

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

Do
Until GUIGetMsg() = -3


тут представленны два варианта для сравнения скорости, т.е. нижний блок можно убрать совсем (который с пиксельсерч)?
по поводу цвета RGB, в данном случае 0x87BEF5, это цвет RGB только записан в Hex верно?
у меня, почемуто скорость поиска через API, составляет 6-10с ( Вин7 х86 с аэро без аэро никакой разницы)

и как в итоге получить координаты найденного пикселя ? у меня толком ничего не получилось, щелчки происходили совершенно в рандомные области экрана никак не связанными с искомым цветом.
т.е вот это
Код:
ConsoleWrite('X = ' & Mod($Offset, $Size[0]) - 1 & @CR)
    ConsoleWrite('Y = ' & Ceiling($Offset / $Size[0]) - 1 & @CR)

нельзя превратить в координаты Х У, нужно MouseGetPosition крутить ?
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Прежде чем кликать мышкой нужно установить опцию
Opt('MouseCoordMode ', 2)


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

Еще для поиска пикселя есть
Код:
PixelSearch

работает практически мгновенно)
 

sunbeam_2001

Новичок
Сообщения
11
Репутация
1
Попробовал пример, который опубликовал Yashied в ответе #7, на этапе "демонстрация" выходит вот что:

rcminebomb.png


То есть, линейные размеры в два раза меньше, чем надо и что-то с цветами.

Код использовал точь-в-точь как в ответе #7 с первой страницы.

В чём может быть дело?
 
Верх