[재]오토핫키 gdip_imagesearch (Gdip library 응용)

프로그래밍/Autohotkey 2020.06.19 댓글 Plorence

목차

준비물
0. 준비
1. 일반적인 사용 방법

1) 설명
2) 사용 예
2. 특정 구역 캡쳐 or 서치
3. 멀티 서치
예시
4. 특정 색 Trans 서치
5. 방향성 주기
6.굵은 픽셀 서치

준비물

  1. gdip+ lib
  2. gdip_imagesearch lib (https://autohotkey.com/boards/viewtopic.php?t=982)

0. 준비

다운로드 혹은 lib를 작성해서 include 합니다.

#include gdip.ahk
#include gdip_imagesearch.ahk

1. 일반적인 사용 방법

먼저 imagesearch 라이브러리의 모든 것인 한 줄의 코드를 설명해드리겠습니다.
변수를 넣는 란에 무엇을 넣는지 알려드릴게요.

1)설명

Gdip_ImageSearch(pBitmapHaystack,pBitmapNeedle,ByRef OutputList=""
,OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans=""
,SearchDirection=1,Instances=1,LineDelim="`n",CoordDelim=",")

이렇게 되있는데요.
일단 영문 설명을 보시죠.

  • pBitmapHaystack : 찾고자 하는 화면이라 생각하시면 됩니다. 주로 특정 프로그램의 캡쳐화면이죠.
  • pBitmapNeedle : 찾고자하는 이미지입니다.
  • OutputList : 찾은 좌표가 출력되는 곳이죠. 기본 설정으로 나온다면 111,222 와 같이 출력됩니다.
  • OuterX1,OuterY1,OuterX2,OuterY2 : 원래 목적은 이 부분을 통해서 특정 좌표를 출력하는 것인데, 작동되진 않습니다. ㅠ 무시하세요.
  • Variation : 이미지서치와 같이 색의 오차값을 나타냅니다.
  • Trans : 특정 색을 투명화 시키고자 할 때 사용합니다.
  • SearchDirection : 서치 방향을 좌지우지하는건데요, 기본은 좌상 -> 우하 방향입니다.
  • Instances : 서치하고자 하는 이미지의 갯수를 입력하는겁니다.
  • LineDelim : outputlist로 출력되는 좌표를 어떻게 나타낼 지 결정하는건데요, 입력된 좌표를 뭘로 구분 하느냐라고 보실 수 있습니다. 신경 안쓰셔도되요.
  • CoordDelim : 마찬가지로 출력되는 좌표의 x좌표와 y좌표의 구분을 어떻게 할 것인가를 건드려줄 수 있는 겁니다만 신경 안쓰셔도 됩니다.
  • 이것만 보고도 사용하실 수 있으신 분은 그냥 보시고도 사용하실 겁니다. 그러면 그냥 대충 읽어보고 사용시면 됩니다.*

2) 사용 예

간단하게 사용자 함수를 작성합니다.

;example>norm_img("1.png",hwnd,vx,vy)

norm_img(image,hwnd, byref vx, byref vy)
{
pToken:=Gdip_Startup() ;gdip를 시작하고
pBitmapHayStack:=Gdip_BitmapFromhwnd(hwnd) ;hwnd값인 프로그램에서부터 그림을 캡쳐한 뒤
pBitmapNeedle:=Gdip_CreateBitmapFromFile(image) ;지정된 이미지도 저렇게 gdip로 땁니다.

if Gdip_ImageSearch(pBitmapHayStack,pBitmapNeedle,list,0,0,0,0,60,,1,1) {  ;그 뒤 이미지서치 함수를 통해서, 값이 나올 경우
StringSplit, LISTArray, LIST, `,  ; list라는 변수에 저장되는데, 출력 양식은 111,222 처럼 저장되니 이렇게 문자열을 쪼갭니다.
vx:=LISTArray1 ;이렇게 하면 listarray0엔 쪼갠 변수 총 갯수 그다음부터 쭉쭉 나오게 되는 것이죠.
vy:=LISTArray2
;; 이 부분에 마우스 클릭 및 다른 기능을 추가하셔도 됩니다.
Gdip_DisposeImage(pBitmapHayStack), Gdip_DisposeImage(pBitmapNeedle) ;초기화하는 부분입니다. 그냥 넣어주시면되요.
Gdip_Shutdown(pToken)
return true
}
else 
{
Gdip_DisposeImage(pBitmapHayStack), Gdip_DisposeImage(pBitmapNeedle)
Gdip_Shutdown(pToken)
return false
}
}

이렇게 짜시면 두 가지 효과를 얻을 수 있습니다.
하나> return 및 false를 통한 서치의 참 거짓 여부.
둘> 참일 경우 vx 및 vy 얻어옴

이 부분을 많이 질문하시는 경우가 많던데, 참일 경우 뭘 하고, 거짓일 경우 뭘 하고, 입력할 때는

if(norm_img("1.png",hwnd,vx,vy)=true)

혹은

if(norm_img("1.png",hwnd,vx,vy)=false)

로 해주시면 됩니다.

단, 실제 사용에 있어서 안된다 싶으시면 위의 추가란에

msgbox,% Gdip_ImageSearch(pBitmapHayStack,pBitmapNeedle,list,0,0,0,0,60,,1,1)

를 해서 무슨 값이 나오는지 보셔야합니다.
숫자 1이 아닌 아래와 같이 나온다면, 해당 오류가 있는 것이죠.

제 경우엔 시행착오를 겪을 때, 주로 참이면서 -1001일 때가 많았던거 같습니다.
파일이 없거나, hwnd이 제대로 입력되지 않았다면, -1001로 됩니다.
그리고, 제대로 찾았을 경우엔 1로 출력됩니다.

; -1001 ==> invalid haystack and/or needle bitmap pointer
; -1002 ==> invalid variation value
; -1003 ==> X1 and Y1 cannot be negative
; -1004 ==> unable to lock haystack bitmap bits
; -1005 ==> unable to lock needle bitmap bits

2. 특정 구역 캡쳐 or 서치

위의 입력할 변수들을 보시면
OuterX1,OuterY1,OuterX2,OuterY2
위와 같이 있는데, 버그가 있어서 사용이 안된다고 했는데, 저건 그냥 무용지물입니다. 검색 끝에 알게됐어요.
때문에, 다른 방법을 사용하여야 합니다.
이번엔 gdip라이브러리 내에 있는 draw_rectangle을 이용할겁니다.

제일먼저 짧은 사각형으로 잘라내는거부터 하겠습니다.

Gdip_CropImage(pBitmap, x, y, w, h)
{
pBitmap2 := Gdip_CreateBitmap(w, h), G2 := Gdip_GraphicsFromImage(pBitmap2) ;gdip로 w h 크기의 비트맵을 만들고 그래픽 핸들은 g2로 한다 정도로 보시면 되겠습니다.
Gdip_DrawImage(G2, pBitmap, 0, 0, w, h, x, y, w, h) ;그리고 g2의 그래픽핸들에 입력받은 pbitmap을 사용해서 0,0,w,h 만큼 그리되, pbitmap의 x,y,w,h 만큼 그려라 정도라고 보시면 됩니다.
Gdip_DeleteGraphics(G2) ; 초기화 하는 것이구요.
Gdip_DisposeImage(G2)
return pBitmap2 ; 새롭게 그려진 pbitmap2를 반환하는 겁니다.
}

;ex>>img_src_rectangle("1.png",hwnd,vx,vy,200,200,100,100) 1.png의 이미지를 200,200 좌표를 기준으로 100,100 크기의 공간에서 검색해라 라는 의미입니다.
img_src_rectangle(image,hwnd,byref vx, byref vy,vvx,vvy,vw,vh) {
pToken:=Gdip_Startup()
pBitmapHayStack1:=Gdip_BitmapFromhwnd(hwnd)
pBitmapNeedle:=Gdip_CreateBitmapFromFile(image)

pBitmapHayStack:=Gdip_CropImage(pBitmapHayStack1, vvx, vvy, vw, vh)

;Gdip_SetBitmapToClipboard(pBitmapHayStack) ;잘 되고 있는지 클립보드로 저장하는 것이고,
;Gdip_SaveBitmapToFile(pBitmapHayStack, "haystack.png") ; 마찬가지로 잘 되고 있는지 혹은 캡쳐할 때 파일로 저장하는겁니다. 이 부분만 다르므로 아래 부분은 복붙합니다.

if Gdip_ImageSearch(pBitmapHayStack,pBitmapNeedle,list,0,0,0,0,60,,1,1) {  ;그 뒤 이미지서치 함수를 통해서, 값이 나올 경우
StringSplit, LISTArray, LIST, `,  ; list라는 변수에 저장되는데, 출력 양식은 111,222 처럼 저장되니 이렇게 문자열을 쪼갭니다.
vx:=LISTArray1 ;이렇게 하면 listarray0엔 쪼갠 변수 총 갯수 그다음부터 쭉쭉 나오게 되는 것이죠.
vy:=LISTArray2
;; 이 부분에 마우스 클릭 및 다른 기능을 추가하셔도 됩니다.
;; 단, 여기서 출력된 부분은 사각형의 좌표 기준이므로 입력되는 변수 vvx와 vvy를 각각 더해야 온전한 좌표를 얻을 수 있습니다.
Gdip_DisposeImage(pBitmapHayStack), Gdip_DisposeImage(pBitmapNeedle) ;초기화하는 부분입니다. 그냥 넣어주시면되요.
Gdip_Shutdown(pToken)
return true
}
else 
{
Gdip_DisposeImage(pBitmapHayStack), Gdip_DisposeImage(pBitmapNeedle)
Gdip_Shutdown(pToken)
return false
}
}

3. 멀티 서치

멀티 서치, 특정 색 Trans 서치, 방향성 주기의 경우 위의 설명대로 Gdip_ImageSearch 의 변수들만 조작해주면 됩니다.
때문에 저 부분만 수정하면 되므로, 전체코드는 생략하겠습니다.

예시

Gdip_ImageSearch(pBitmapHayStack,pBitmapNeedle,list,0,0,0,0,60,,1,10)

저기 10 부분이 찾을 이미지의 갯수입니다. 0으로 할 경우 모든 이미지를 찾는데,
속도가 너무 느려지기 때문에, 비추천입니다.

;기본 설정을 통해 두 개의 이미지를 찾게 될 경우
;list 변수 내의 텍스트
110,111
222,333

이와같이 출력되므로
StringSplit을 통해 좌표를 처리하는 후처리가 필요해집니다
이 출력된 list라는 변수를
StringSplit, LISTArray, LIST, `n
하게 될 경우 listarray0 엔 총 서치된 이미지 갯수가 격납됩니다.

num:=1
loop,%listarray0%
{
msgbox,% listarray%num%
num++
}

이렇게 하면 모든 좌표들이 차례대로 뜨는 것이죠.

4. 특정 색 Trans 서치

멀티 서치, 특정 색 Trans 서치, 방향성 주기의 경우 위의 설명대로 Gdip_ImageSearch 의 변수들만 조작해주면 됩니다.
때문에 저 부분만 수정하면 되므로, 전체코드는 생략하겠습니다.

Gdip_ImageSearch(pBitmapHayStack,pBitmapNeedle,list,0,0,0,0,60,**0xFFFFFF**,1,1)

0xFFFFFF 부분에 원하는 색의 RGB를 입력해줍니다. 뭐.. 따로 설명해 드릴 건 없는거 같습니다.

5. 방향성 주기

멀티 서치, 특정 색 Trans 서치, 방향성 주기의 경우 위의 설명대로 Gdip_ImageSearch 의 변수들만 조작해주면 됩니다.
때문에 저 부분만 수정하면 되므로, 전체코드는 생략하겠습니다.

Gdip_ImageSearch(pBitmapHayStack,pBitmapNeedle,list,0,0,0,0,60,,1,1)

마지막에서 두 번째 1이 있는 저 부분입니다.

서치 방향을 결정해주는 건데요. 대충 이렇게 8가지 종류가 있습니다.

6. 굵은 픽셀 서치

정말 유용해보이는 gdip_imagesearch 라이브러리에도 제가 사용해본 바 주의사항이 두 가지 정도 있는거 같습니다.
하나, 위에서 있던 OuterX1,OuterY1,OuterX2,OuterY2 가 작동 안되고 쓸모없는것...
, 그림이 너무 작으면 제대로 안됩니다.

제가 해봤을 당시 8x8의 단색 이미지를 서치 못하더군요. return이 -1001으로 되었던거 같습니다. 단 이 오류는 첫 실행에만 제대로 작동하지 않고, 이상하게 두 번째부턴 제대로 작동합니다. 한 번에 찾진 못하더군요.
그래서 찾은 방법이 굵은 픽셀 서치입니다. 사실 찾고자 하는 이미지가 작고 단색일 때만 빨리 할 수 있는거죠. 이미지 없이 색만으로 직접 그려서 합니다.(저의 경우엔 yes24티케팅 프로그램에서 빈 좌석 찾기에 사용했습니다.)
8x8까진 해봤는데 더 작은것들도 되는진 모르겠네요.

그럼 짧은 코드들부터 적어보겠습니다.

RGB2ARGB(RGB) { ;;아래에 쓰일 RGB코드를 ARGB코드로 바꿔주는 사용자 함수입니다. 색을 칠할 때 ARGB로 사용하기 때문이죠
    return "0XFF" RGB
}

draw_rectangle_with_pixel(RGB,w=8, h=8) ;gdip_draw를 이용해 지정한 범위만큼 색상을 채우는겁니다.
{
ARGB:=RGB2ARGB(RGB)
If !pToken := Gdip_Startup()
{
    MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
    ExitApp
}
pBitmap := Gdip_CreateBitmap(w, h) , G := Gdip_GraphicsFromImage(pBitmap)
pBrush_acolor := Gdip_BrushCreateSolid(ARGB) ;이 부분들은 오토핫키 외국포럼의 gdip_tutorial을 참고했습니다.
Gdip_FillRectangle(G, pBrush_acolor, 0, 0, w, h) ;말그대로 붓을 들고 칠을 한다 라고 생각하시면 되겠네요.
Gdip_DeleteBrush(pBrush)
;Gdip_SaveBitmapToFile(pBitmap, "sFile.png") ;잘 되는지 확인하고 싶으시면 이부분의 주석을 해제하고 사용해보세요
Gdip_DeleteGraphics(G)
Gdip_Shutdown(pToken)
return pBitmap
}

draw_rectangle_pixel_search(rgb_pixel,hwnd,byref vx,byref vy,w=8,h=8) {
pToken:=Gdip_Startup()
pBitmapHayStack:=Gdip_BitmapFromhwnd(hwnd)
pbitmapneedle:=draw_rectangle_with_pixel(rgb_pixel,w,h)

if Gdip_ImageSearch(pBitmapHayStack,pBitmapNeedle,list,0,0,0,0,60,,1,1) {
StringSplit, LISTArray, LIST, `,  ; list라는 변수에 저장되는데, 출력 양식은 111,222 처럼 저장되니 이렇게 문자열을 쪼갭니다.
vx:=LISTArray1 ;이렇게 하면 listarray0엔 쪼갠 변수 총 갯수 그다음부터 쭉쭉 나오게 되는 것이죠.
vy:=LISTArray2
;; 이 부분에 마우스 클릭 및 다른 기능을 추가하셔도 됩니다.
Gdip_DisposeImage(pBitmapHayStack), Gdip_DisposeImage(pBitmapNeedle)
Gdip_Shutdown(pToken)}
return true
else {
Gdip_DisposeImage(pBitmapHayStack), Gdip_DisposeImage(pBitmapNeedle)
Gdip_Shutdown(pToken)
return false
}
}

사용하실 때는

draw_rectangle_pixel_search("FF6600",hwnd,vx,vy,8,8)

이렇게 사용하시면 되겠네요.

마치며,

이상 비활성 서치 라이브러리인 gdip_imagesearch 를 사용하는 것이었습니다.

※이 게시글의 내용은 저도 네이버 블로그에서 퍼왔으며, 출처를 아시는 분은 댓글 부탁드립니다.

출처를 적지 못해 죄송합니다.

(삭제했다가 요청으로 다시 서식을 작성해 올렸습니다.)

댓글