Что нового

Поиск всех координат одного цвета c допустимым диапазоном как в PixelSearch

iamOmg

Новичок
Сообщения
97
Репутация
2
Нашел на просторах форума вот этот скрипт и сразу вопрос возник, как в нём можно реализовать допустимый диапазон погрешности цветов ровно как в функции PixelSearch? :scratch:

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

$t = TimerInit()
$test = _PixelGetArray(200, 200, 100, 100, 0xFFFFFF)
ConsoleWrite(TimerDiff($t) & @LF)
_ArrayDisplay($test)

Func _PixelGetArray($iX, $iY, $iWidth, $iHeight, $iColor, $hWnd = 0)
    Local $aPixels[$iWidth * $iHeight + 1][2]
    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, $SRCCOPY)
    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_ReleaseDC($hWnd, $hDC)
    Local $iSize = $iWidth * $iHeight
    Local $tBits = DllStructCreate("dword[" & $iSize & "]")
    _WinAPI_GetBitmapBits($hBitmap, 4 * $iSize, DllStructGetPtr($tBits))
    Local $iCount = 0
    For $i = 1 To $iSize
        If BitAND(DllStructGetData($tBits, 1, $i), 0x00FFFFFF) = $iColor Then
            $iCount += 1
            $aPixels[$iCount][0] = Mod($i, $iWidth) - 1
            If $aPixels[$iCount][0] = -1 Then $aPixels[$iCount][0] = $iWidth - 1
            $aPixels[$iCount][1] = Ceiling($i / $iWidth) - 1
        EndIf
    Next
    ReDim $aPixels[$iCount + 1][2]
    $aPixels[0][0] = $iCount
    Return $aPixels
EndFunc   ;==>_PixelGetArray
 

InnI

AutoIT Гуру
Сообщения
4,974
Репутация
1,459
iamOmg
вот этот скрипт
Это "устаревшая" функция. На её основе был разработан более быстрый аналог: Ответ #19 (под спойлером)

как в нём можно реализовать
Разложить цвет на составляющие RGB и сравнивать каждую составляющую на вхождение в диапазон.


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

Добавлена возможность задания оттенка $i_Shade. Скорость поиска с оттенком в 2-3 раза ниже, чем без него
Код:
#include <WinAPIGdi.au3>

$iT = TimerInit()
$aPixels = _PixelGetArray(100, 100, 10, 10, 0x000000, 10)
ConsoleWrite(TimerDiff($iT) & @LF)

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

Func _PixelGetArray($i_X, $i_Y, $i_Width, $i_Height, $i_Color, $i_Shade = 0, $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
  $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, 0x00CC0020) ; $SRCCOPY
  _WinAPI_DeleteDC($h_MemDC)
  _WinAPI_ReleaseDC($h_Wnd, $h_DC)
  $t_Bits = DllStructCreate('dword[' & $i_Size & ']')
  _WinAPI_GetBitmapBits($h_Bitmap, 4 * $i_Size, DllStructGetPtr($t_Bits))
  _WinAPI_DeleteObject($h_Bitmap)
  If $i_Shade Then
    Local $a_RGB[3] = [BitAND(BitShift($i_Color, 16), 0xFF), BitAND(BitShift($i_Color, 8), 0xFF), BitAND($i_Color, 0xFF)]
    For $i = 0 To $i_Height - 1
      For $j = 0 To $i_Width - 1
        $i_Index += 1
        Local $i_Clr = BitAND(DllStructGetData($t_Bits, 1, $i_Index), 0x00FFFFFF)
        Switch BitAND(BitShift($i_Clr, 16), 0xFF)
          Case $a_RGB[0] - $i_Shade To $a_RGB[0] + $i_Shade
            Switch BitAND(BitShift($i_Clr, 8), 0xFF)
              Case $a_RGB[1] - $i_Shade To $a_RGB[1] + $i_Shade
                Switch BitAND($i_Clr, 0xFF)
                  Case $a_RGB[2] - $i_Shade To $a_RGB[2] + $i_Shade
                    $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
                EndSwitch
            EndSwitch
        EndSwitch
      Next
    Next
  Else
    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
  EndIf
  ReDim $a_Pixels[$a_Pixels[0][0] + 1][2]
  Return $a_Pixels
EndFunc   ;==>_PixelGetArray
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,323
InnI,
ИМХО, Если создать структуру
Код:
$t_Bits = DllStructCreate('byte[' & $i_Size * 4 & ']')
и в цикле задать Step 4, то должно немного ускориться.
 

InnI

AutoIT Гуру
Сообщения
4,974
Репутация
1,459
madmasles
как и предыдущую
Предыдущая в четыре раза меньше. Все биты окажутся в первой четверти и три четверти будут пустыми.
Всё равно не понимаю.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,323

InnI

AutoIT Гуру
Сообщения
4,974
Репутация
1,459
Не обратил внимания на "byte". Ну, да. Размер тот же. Но переделать не получилось. Туго у меня со структурами :(
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,323
InnI [?]
Но переделать не получилось.
Без $i_Shade, добавить не сложно.
Код:
Func _PixelGetArrayByte($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, $p_Bits, $i_Index = 1, _
			$a_BGR[3] = [BitAND($i_Color, 0xFF), BitAND(BitShift($i_Color, 8), 0xFF), BitAND(BitShift($i_Color, 16), 0xFF)]

	If $h_Wnd Then
		If BitAND(WinGetState($h_Wnd), 16) Then Return SetError(-1, 0, $a_Pixels)
	EndIf
	$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('byte[' & $i_Size * 4 & ']')
	$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
			Switch DllStructGetData($t_Bits, 1, $i_Index)
				Case $a_BGR[0]
					Switch DllStructGetData($t_Bits, 1, $i_Index + 1)
						Case $a_BGR[1]
							Switch DllStructGetData($t_Bits, 1, $i_Index + 2)
								Case $a_BGR[2]
									$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
							EndSwitch
					EndSwitch
			EndSwitch
			$i_Index += 4
		Next
	Next
	ReDim $a_Pixels[$a_Pixels[0][0] + 1][2]
	Return $a_Pixels
EndFunc   ;==>_PixelGetArrayByte
Или так (немного меньше кода).
Код:
Func _PixelGetArrayByte($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, $p_Bits, $i_Index = -3, _
			$a_BGR[3] = [BitAND($i_Color, 0xFF), BitAND(BitShift($i_Color, 8), 0xFF), BitAND(BitShift($i_Color, 16), 0xFF)]

	If $h_Wnd Then
		If BitAND(WinGetState($h_Wnd), 16) Then Return SetError(-1, 0, $a_Pixels)
	EndIf
	$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('byte[' & $i_Size * 4 & ']')
	$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 += 4
			For $q = 0 To 2
				Switch DllStructGetData($t_Bits, 1, $i_Index + $q)
					Case $a_BGR[$q]
;~ 						OK
					Case Else
						ContinueLoop 2
				EndSwitch
			Next
			$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
		Next
	Next
	ReDim $a_Pixels[$a_Pixels[0][0] + 1][2]
	Return $a_Pixels
EndFunc   ;==>_PixelGetArrayByte
 

Yashied

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

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,323
Еще мне не понятно, как, например, PixelSearch() высчитывает shade-variation. Если, к примеру красный равен 250, а shade-variation 10, то, если нижняя граница 240 реальна, верхняя 260 - не реальна. Как ее правильно считать?


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

Yashied [?]
Если два цикла "For" перевести на машинный код
Уметь бы еще это делать...
 

Prog

Продвинутый
Сообщения
622
Репутация
80

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Я обычно пишу DLL а потом вытаскиваю из нее машинный код функции. Просто без машинного кода дальнейшая оптимизация функции, это топтание на месте.
 

InnI

AutoIT Гуру
Сообщения
4,974
Репутация
1,459
madmasles
Без $i_Shade, добавить не сложно.
Так весь смысл темы в том, чтобы оттенок добавить.

А по поводу быстродействия без оттенка результаты интересные: чем больше найденных пикселей, тем _PixelGetArrayByte() медленнее работает по сравнению с _PixelGetArray(). Если число найденных пикселей составляет примерно 20% от общего количества, то _PixelGetArrayByte() на 25% быстрее _PixelGetArray(). Если найденных 80%, то _PixelGetArrayByte() работает медленнее _PixelGetArray() на те же 25%. А вот аналог _PixelGetArrayByte(), в которой "немного меньше кода", в любом случае работает медленнее _PixelGetArray().

Yashied
Если два цикла "For" перевести на машинный код или запихнуть в DLL
А чего только циклы? Пихать, так всё целиком. Скорость вообще бешеная будет.

Prog
Это не так сложно как кажется.
Так что же вы до сих пор не добавили подобную функцию в свою DLL? ;)
 
Автор
iamOmg

iamOmg

Новичок
Сообщения
97
Репутация
2
InnI сказал(а):
Это "устаревшая" функция. На её основе был разработан более быстрый аналог: Ответ #19 (под спойлером)

Премного благодарен, у меня этот вариант работает немного быстрее, что уже не плохо.

InnI сказал(а):
Добавлена возможность задания оттенка $i_Shade. Скорость поиска с оттенком в 2-3 раза ниже, чем без него
А вот это плохо, ибо на моём 2ядерном i5 поиск без оттенка занимает 1.8 секунды, видимо и вправду нужно переводить это в dll, но всё равно спасибо


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

Yashied сказал(а):
Если два цикла "For" перевести на машинный код или запихнуть в DLL, то функция ускорится на порядок.
Насколько это сложная задача и есть ли люди которые смогут это сделать?
 

valldar

Новичок
Сообщения
32
Репутация
2
Как в этом скрипте добавить шаг?
Т.е. например у меня есть 20 шт красных квадратиков размером 10х10 пикселей.
Как мне получить ровно 20 координат этих квадратиков, а не не каждого пикселя в одном квадратике?
 
Верх