Что нового

Поиск всех координат одного цвета 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,548
Репутация
1,249
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,320
InnI,
ИМХО, Если создать структуру
Код:
$t_Bits = DllStructCreate('byte[' & $i_Size * 4 & ']')
и в цикле задать Step 4, то должно немного ускориться.
 

InnI

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

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
InnI [?]
Предыдущая в четыре раза меньше
С чего бы?
Код:
$i_Size = 123
$t_BitsDd = DllStructCreate('dword[' & $i_Size & ']')
$t_BitsBt = DllStructCreate('byte[' & $i_Size * 4 & ']')
ConsoleWrite('Size $t_BitsDd: ' & DllStructGetSize($t_BitsDd) & @LF)
ConsoleWrite('Size $t_BitsBt: ' & DllStructGetSize($t_BitsBt) & @LF)
 

InnI

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

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,320
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,716
Если два цикла "For" перевести на машинный код или запихнуть в DLL, то функция ускорится на порядок.
 

madmasles

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


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

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

Prog

Осваивающий
Сообщения
342
Репутация
32

Yashied

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

InnI

AutoIT Гуру
Сообщения
4,548
Репутация
1,249
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 координат этих квадратиков, а не не каждого пикселя в одном квадратике?
 
Верх