Что нового

PicInPic - поиск картинки в картинке

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
AutoIt: 3.3.8 +

Категория: Графика, GDI+, Строки

Описание: Функция _PicInPic() предназначена для поиска картинки в картинке (Picture In Picture).

Данная функция появилась в результате экспериментов по поиску бинарных данных в текстовом виде. Сразу хочу заметить, что если нужен мгновенный поиск на большой картинке или необходимо найти несколько одинаковых картинок сразу, то нужно использовать BmpSearch UDF.

Параметрами функции являются две картинки: первая - на которой искать, вторая - которую искать. В качестве любой из картинок может быть либо объект gdi32 (скриншот), либо объект GDI+ (картинка из файла), либо просто путь к файлу с картинкой. В случае успешного поиска функция возвращает массив координат левого верхнего угла искомой картинки относительно левого верхнего угла картинки, на которой производился поиск. В @extended записывается время поиска в миллисекундах. При проблемах, а также при неудачном поиске, устанавливается @error. @error = 1 означает, что картинка не найдена. Остальные ошибки указаны в описании функции.

Обращаю внимание:
- функция требует подключения #include <GDIPlus.au3> или #include <ScreenCapture.au3>
- для обеспечения успешного поиска картинку нужно сохранять в форматах без потерь: BMP или PNG
- при самостоятельном создании объектов, во избежание утечек памяти, не забывайте их удалять после работы функции, когда объекты больше не нужны

Код:
Код:
; ==============================================================================================================
; Имя функции : _PicInPic
; Описание    : Поиск картинки в картинке
; Синтаксис   : _PicInPic($vPic1, $vPic2)
; Параметры   : $vPic1  - картинка, в которой производится поиск (объект GDI+ или объект gdi32 или путь к файлу)
;             : $vPic2  - картинка, которую необходимо найти (объект GDI+ или объект gdi32 или путь к файлу)
; Возвращает  : Успех   - массив координат левого верхнего угла искомой картинки
;             :           относительно картинки, в которой производится поиск
;             :         $aArray[0] - X координата
;             :         $aArray[1] - Y координата
;             :         @extended  - время поиска в миллисекундах
;             : Неудача - 0 и устанавливает @error
;             :         @error = 1 - картинка не найдена
;             :         @error = 2 - файл $vPic1 не найден
;             :         @error = 3 - файл $vPic2 не найден
;             :         @error = 4 - ошибка определения размера $vPic1
;             :         @error = 5 - ошибка определения размера $vPic2
;             :         @error = 6 - картинка $vPic1 меньше картинки $vPic2
; Автор       : InnI
; Примечание  : Необходимо подключение #include <GDIPlus.au3> или #include <ScreenCapture.au3>
; ==============================================================================================================
Func _PicInPic($vPic1, $vPic2)
  Local $fDel1, $fDel2, $hImg1, $hImg2, $iTime = TimerInit()
  _GDIPlus_Startup()

  If IsString($vPic1) Then
    If Not FileExists($vPic1) Then Return SetError(2, _GDIPlus_Shutdown(), 0)
    $vPic1 = _GDIPlus_BitmapCreateFromFile($vPic1)
    $fDel1 = 1
  Else
    $hImg1 = _GDIPlus_BitmapCreateFromHBITMAP($vPic1)
    If Not @error Then
      $vPic1 = $hImg1
      $fDel1 = 1
    EndIf
  EndIf
  If IsString($vPic2) Then
    If Not FileExists($vPic2) Then Return SetError(3, _GDIPlus_Shutdown(), 0)
    $vPic2 = _GDIPlus_BitmapCreateFromFile($vPic2)
    $fDel2 = 1
  Else
    $hImg2 = _GDIPlus_BitmapCreateFromHBITMAP($vPic2)
    If Not @error Then
      $vPic2 = $hImg2
      $fDel2 = 1
    EndIf
  EndIf

  Local $iW1 = _GDIPlus_ImageGetWidth($vPic1)
  If @error Then Return SetError(4, _GDIPlus_Shutdown(), 0)
  Local $iW2 = _GDIPlus_ImageGetWidth($vPic2)
  If @error Then Return SetError(5, _GDIPlus_Shutdown(), 0)
  Local $iH1 = _GDIPlus_ImageGetHeight($vPic1)
  Local $iH2 = _GDIPlus_ImageGetHeight($vPic2)
  If $iW1 < $iW2 Or $iH1 < $iH2 Then
    If $fDel1 Then _GDIPlus_BitmapDispose($vPic1)
    If $fDel2 Then _GDIPlus_BitmapDispose($vPic2)
    Return SetError(6, _GDIPlus_Shutdown(), 0)
  EndIf

  Local $tBits1 = _GDIPlus_BitmapLockBits($vPic1, 0, 0, $iW1, $iH1, $GDIP_ILMREAD, $GDIP_PXF24RGB)
  Local $iDiff1 = Abs(DllStructGetData($tBits1, "Stride")) - $iW1 * 3
  Local $sText1 = StringTrimLeft(DllStructGetData(DllStructCreate("byte[" & ($iW1 * 3 + $iDiff1) * $iH1 & "]", DllStructGetData($tBits1, "Scan0")), 1), 2)
  Local $aLines1[$iH1], $c = 0, $iLen = $iW1 * 6, $iStep = $iLen + $iDiff1 * 2
  For $i = 1 To StringLen($sText1) Step $iStep
    $aLines1[$c] = StringMid($sText1, $i, $iLen)
    $c += 1
  Next
  _GDIPlus_BitmapUnlockBits($vPic1, $tBits1)
  Local $tBits2 = _GDIPlus_BitmapLockBits($vPic2, 0, 0, $iW2, $iH2, $GDIP_ILMREAD, $GDIP_PXF24RGB)
  Local $iDiff2 = Abs(DllStructGetData($tBits2, "Stride")) - $iW2 * 3
  Local $sText2 = StringTrimLeft(DllStructGetData(DllStructCreate("byte[" & ($iW2 * 3 + $iDiff2) * $iH2 & "]", DllStructGetData($tBits2, "Scan0")), 1), 2)
  Local $aLines2[$iH2], $c = 0, $iLen = $iW2 * 6, $iStep = $iLen + $iDiff2 * 2
  For $i = 1 To StringLen($sText2) Step $iStep
    $aLines2[$c] = StringMid($sText2, $i, $iLen)
    $c += 1
  Next
  _GDIPlus_BitmapUnlockBits($vPic2, $tBits2)

  Local $iMid = Floor($iH2 / 2), $iLen1 = StringLen($aLines1[0]), $iLen2 = StringLen($aLines2[0]), $iPos, $iStart, $iLine, $aXY[2]
  For $i1 = $iMid To $iH1 - 1
    $iStart = 1
    While $iStart <= $iLen1
      $iPos = StringInStr($aLines1[$i1], $aLines2[$iMid], 1, 1, $iStart)
      If $iPos Then
        If Mod($iPos - 1, 6) Then
          $iStart = $iPos + 2
          ContinueLoop
        EndIf
        If $i1 + $iH2 - $iMid > $iH1 Then Return SetError(1, _GDIPlus_Shutdown(), 0)
        $iLine = $i1 - $iMid
        For $i2 = 0 To $iH2 - 1
          If StringMid($aLines1[$iLine], $iPos, $iLen2) == $aLines2[$i2] Then
            $iLine += 1
          Else
            ExitLoop
          EndIf
        Next
        If $i2 = $iH2 Then
          $aXY[0] = ($iPos - 1) / 6
          $aXY[1] = $i1 - $iMid
          If $fDel1 Then _GDIPlus_BitmapDispose($vPic1)
          If $fDel2 Then _GDIPlus_BitmapDispose($vPic2)
          _GDIPlus_Shutdown()
          Return SetError(0, TimerDiff($iTime), $aXY)
        Else
          $iStart = $iPos + 6
        EndIf
      Else
        ExitLoop
      EndIf
    WEnd
  Next
  If $fDel1 Then _GDIPlus_BitmapDispose($vPic1)
  If $fDel2 Then _GDIPlus_BitmapDispose($vPic2)
  Return SetError(1, _GDIPlus_Shutdown(), 0)
EndFunc  ;==>_PicInPic

Примеры:
Код:
; Пример поиска картинки на всём экране
; картинка находится в файле в папке скрипта

#include <ScreenCapture.au3>

$hBmp = _ScreenCapture_Capture()
$aCoord = _PicInPic($hBmp, "pic.png")
If @error Then
  MsgBox(0, "Error", "Ошибка: " & @error)
Else
  MsgBox(0, "Координаты", "X = " & $aCoord[0] & @CRLF & _
                          "Y = " & $aCoord[1] & @CRLF & _
                          "Время: " & @extended & "мс")
EndIf
_WinAPI_DeleteObject($hBmp)
Код:
; Пример отслеживания перемещения картинки,
; загруженной из файла, по всему экрану
; с выводом координат в консоль SciTE

#include <ScreenCapture.au3>

_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile("pic.png")
Global $X = -1, $Y = -1
While Sleep(11)
  $hBmp = _ScreenCapture_Capture()
  $aXY = _PicInPic($hBmp, $hImage)
  If Not @error Then
    If $aXY[0] <> $X Or $aXY[1] <> $Y Then
      ConsoleWrite($aXY[0] & ":" & $aXY[1] & @CRLF)
      $X = $aXY[0]
      $Y = $aXY[1]
    EndIf
  EndIf
  _WinAPI_DeleteObject($hBmp)
WEnd

Тестирование производительности:
Тестовое окружение: Core i5 3.20 GHz, Windows 7, AutoIt 3.3.14.2
- поиск FullHD скриншота на самом скриншоте -> 150мс
- поиск иконки в трее на FullHD скриншоте -> 110мс
- поиск кнопки "Изменение цветов" в панели Paint -> 12мс
- поиск правого нижнего пикселя в квадрате 100х100 -> 1мс

Источник: autoit-script.ru

Автор: InnI
 

Kybik

Новичок
Сообщения
3
Репутация
0
Добрый день, возможно ли реализовать не точный поиск, а с отклонением, как в imageSearch.dll ?
 
Автор
I

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Поиск с отклонением, реализованный на чистом AutoIt, будет очень медленным и, соответственно, бесполезным с точки зрения практического применения.
 

adiv18

Новичок
Сообщения
5
Репутация
0
Добрый день.
А можно искать цифры в картинке ?
 
Автор
I

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Если цифры являются картинками, то можно.
Если вы имеете в виду распознавание, то нет.
 
Верх