Что нового

Получить список цветов скриншота запущенной не активной программы

mef-t

Осваивающий
Сообщения
306
Репутация
30
Добрый день.

Долго и упорно, в силу незнания winAPI, пытался реализовать задачу из заголовка.
Получилось следующее:

Код:
#include <WinAPIEx.au3>
#include <Array.au3>
#include <Color.au3>

$hWnd = WinGetHandle("[TITLE:Plants vs. Zombies]"); Вписать заголовок окна

Local $hDDC, $hCDC, $hBMP
$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)

$color = ''

   For $Width = 1 To $iWidth
	  For $Height = 1 To $iHeight
		 $l = _WinAPI_GetPixel($hCDC, $Width, $Height)
		 If Not IsDeclared($l & '_') Then
			Assign($l & '_', 0, 1)
			$color &= $l & '_'
		 EndIf
	  Next
   Next

_WinAPI_ReleaseDC($hWnd, $hDDC)
_WinAPI_DeleteDC($hCDC)
_WinAPI_DeleteObject($hBMP) ; очищаем битовый массив

Local $aUnique = StringSplit(StringTrimRight($color, 1), "_", 2)

;~ For $i = 0 To Ubound($aUnique)-1
;~    $aUnique[$i]='0x'&Hex(_ColorGetBlue('0x'&$aUnique[$i]),2) & Hex(_ColorGetGreen('0x'&$aUnique[$i]),2) & Hex(_ColorGetRed('0x'&$aUnique[$i]),2)
;~ Next
_ArrayDisplay($aUnique)

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

Просьба помочь.
Спасибо.
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
mef-t

http://autoit-script.ru/index.php?topic=15740.msg97689#msg97689
http://autoit-script.ru/index.php?topic=11965.0
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Спасибо. Я почитаю.
Но... в примере берутся картинки из файла. Я же получаю их другим способом.
Я понимаю, что можно сохранить, снова взять, но я бы хотел отказаться от лишних шагов.
У меня не получилось перевести $hBitmap картинки в формат GDIPlus.
Собственно говоря, по этой причине я и мучаюсь.
 
Автор
mef-t

mef-t

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

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
mef-t
Например,

Код:
#include <GDIPlus.au3>
#include <WinAPI.au3>
#include <Array.au3>
#include <Color.au3>

$sClass = 'SciTEWindow'


_GDIPlus_Startup()
$hBitmap = _Capture_Window(WinGetHandle('[CLASS:' & $sClass & ']', ''), @DesktopHeight, @DesktopWidth)
$Width = _GDIPlus_ImageGetWidth($hBitmap)
$Height = _GDIPlus_ImageGetHeight($hBitmap)

$tMap = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $Width, $Height, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
$bData = DllStructGetData(DllStructCreate('byte[' & ($Width * $Height * 4) & ']', DllStructGetData($tMap, 'Scan0')), 1)
_GDIPlus_BitmapUnlockBits($hBitmap, $tMap)
_GDIPlus_BitmapDispose($hBitmap)

$bData = StringTrimLeft($bData, 2)


_GDIPlus_Shutdown()

$bData = '######FF' & $bData

$aTemp = StringRegExp($bData, '(\S{1,6})FF', 3)
$aTemp[0] = UBound($aTemp) - 1
$step = 1
For $i = 1 To $aTemp[0] Step $step
	Assign($aTemp[$i], Eval($aTemp[$i]) + 1)
Next

Dim $uArray[$aTemp[0]][2] = [[0]]

For $i = 1 To $aTemp[0] Step $step
	If Eval($aTemp[$i]) > 0 Then
		$uArray[0][0] += 1
		$uArray[$uArray[0][0]][0] = $aTemp[$i]
		$uArray[$uArray[0][0]][1] = Eval($aTemp[$i])
		Assign($aTemp[$i], -1)
	EndIf
Next
ReDim $uArray[$uArray[0][0] + 1][2]

_ArraySort($uArray, 1, 1, 0, 1)

For $i = 1 To UBound($uArray) - 1
	$uArray[$i][0] = '0x' & Hex(_ColorGetBlue('0x' & $uArray[$i][0]), 2) & Hex(_ColorGetGreen('0x' & $uArray[$i][0]), 2) & Hex(_ColorGetRed('0x' & $uArray[$i][0]), 2)
Next

ConsoleWrite('Высота: ' & $Height & @CRLF)
ConsoleWrite('Ширина: ' & $Width & @CRLF)
ConsoleWrite('Всего цветов: ' & $aTemp[0] & @CRLF)
ConsoleWrite('Уникальных цветов: ' & $uArray[0][0] & @CRLF)
ConsoleWrite('Фон: ' & $uArray[1][0] & @CRLF)

_ArrayDisplay($uArray)


Func _Capture_Window($hWnd, $w, $h)
	Local $hDC_Capture = _WinAPI_GetWindowDC($hWnd)
	Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC_Capture)
	Local $hHBitmap = _WinAPI_CreateCompatibleBitmap($hDC_Capture, $w, $h)
	Local $hObj = _WinAPI_SelectObject($hMemDC, $hHBitmap)
	DllCall('user32.dll', 'int', 'PrintWindow', 'hwnd', $hWnd, 'handle', $hMemDC, 'int', 0)
	_WinAPI_DeleteDC($hMemDC)
	Local $hObject = _WinAPI_SelectObject($hMemDC, $hObj)
	_WinAPI_ReleaseDC($hWnd, $hDC_Capture)
	Local $hBmp = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)
	_WinAPI_DeleteObject($hHBitmap)
	Return $hBmp
EndFunc   ;==>_Capture_Window
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Этот вариант я пробовал и даже начинал с него. Но что то не получилось.

WSWR, пробую реализовать через Ваш пример.
Но у Вас есть строчка
Код:
Dim $uArray[$aTemp[0]][2] = [[0]]
на которую у меня выходит ошибка: Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
Тема http://autoit-script.ru/index.php?topic=11965.0

Вы не могли бы ее объяснить?

----------------------------------------

За пример большое спасибо. Пробую


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

Спасибо.
В этом примере ошибок нет.

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

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

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
mef-t [?]
В силу того, что планирую сравнивать списки цветов нескольких картинок
А зачем делать эти списки? Можно сравнивать хеш-суммы битмапов окна и образцов-скриншотов. Могу написать пример.
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Мне нужно получить каких цветов одного изображения нет в других изображениях.
Для примера, прошел по карте какой-нить игры, наделал скриншотов, определил, в каких зонах безопасно, а в каких нет визуально, и ищешь цвета зон, в которых не безопасно.
А затем уже применяешь алгоритмы с использованием полученных цветов.


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

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

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
mef-t [?]
Если хэш-суммы позволят получить такие цвета, то буду благодарен за пример.
Если сделать скриншоты определенного участка окна со всеми возможными вариантами как образцы, получить их хэш-суммы, сохранить файлами или базой, то потом можно в реальном времени скриншотить этот участок, сравнивать с образцами и, если есть совпадение, что-то делать.
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
На сколько я понял, если речь идет о сравнении в строго определенной области.
Это несколько не то.
Объект с уникальным цветом может появляться в различных местах.
Хотя Ваш вариант я запомню, так как для некоторых целей он действительно хорош.

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

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
mef-t сказал(а):
К сожалению, по скорости мой вариант не уступает данному, а хотелось бы быстрее.
Спасибо дальше буду разбираться.

В коде есть переменная $step - шаг в цикле
Можно ее увеличить, и тогда скорость работы тоже увеличится: обрабатываться будут не все пиксели, а через этот шаг.

И еще можно посмотреть эти темы:
http://autoit-script.ru/index.php?topic=14905.0
http://autoit-script.ru/index.php?topic=13686.0
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
WSWR,
Можно немного увеличить скорость, если цвета забирать только из клиентской области окна. Для этого в Вашем коде надо поменять:
Код:
;~ ...
$sClass = 'SciTEWindow'
$hBitmap = _Capture_Window(WinGetHandle('[CLASS:' & $sClass & ']', ''), @DesktopHeight, @DesktopWidth);здесь надо поменять местами $w и $h
;~ ...
Local $hDC_Capture = _WinAPI_GetWindowDC($hWnd)
;~ ...
 DllCall('user32.dll', 'int', 'PrintWindow', 'hwnd', $hWnd, 'handle', $hMemDC, 'int', 0)
;~  ...
На:
Код:
;~ ...
$hWin = WinGetHandle('[Class:SciTEWindow]');или иначе
$aClient = WinGetClientSize($hWin)
$hBitmap = _Capture_Window($hWin, $aClient[0], $aClient[1])
;~ ...
Local $hDC_Capture = _WinAPI_GetDC($hWnd)
;~ ...
 DllCall('user32.dll', 'int', 'PrintWindow', 'hwnd', $hWnd, 'handle', $hMemDC, 'int', 1);PW_CLIENTONLY -> http://msdn.microsoft.com/en-us/library/windows/desktop/dd162869(v=vs.85).aspx
;~  ...
Но, 99% времени уходит на перебор цветов в циклах.
 
Автор
mef-t

mef-t

Осваивающий
Сообщения
306
Репутация
30
Про область: я сразу это справил, только размеры брал уже внутри функции.
От 20 до 30 секунд уходит на обработку изображения 800 на 600.
5 секунд из них идет преобразование цветов в формат 0x......
Предположу, что часть времени уходит на запись массива.

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