📣 더 깊은 내용이 담긴 '프날 오토핫키 강좌 v2'의 서적판이 공개되었습니다.
가장 최신의 오토핫키를 담은 '프날 오토핫키 강좌 v2'를 책으로 만나보세요!
v2 페이지에 공개된 분량보다 더욱 깊은 내용을 처음이라도 괜찮아, 오토핫키 프로그래밍을 통해 배울 수 있습니다.
전국 온/오프라인 서점에서 만나요!
책 더 알아보기 및 구매하기
[프날 오토핫키] 두 이미지 비교하기(픽셀 값 빠르게 얻기)
안녕하세요. 우선 본 글은 질문답변란에 올라온 글에 답변해드리기 위해서 쓴 글이니 검색 통해서 오신 분들은 참고 바랍니다. Gdip 강좌를 보고 오시면 아래 내용을 이해하는데 수월합니다. 아래 더보기 버튼을 누르면 Gdip 강좌 전체 링크가 나옵니다.
질문의 요지는 "두 이미지를 비교하여 서로 다른 부분의 좌푯값을 얻고싶다"입니다. 물론 두 이미지를 비교하는 것이기 때문에 Gdip_ImageSearch()를 생각할 수 있으나, 이 경우엔 좌푯값을 얻기에는 다소 곤란해지게 됩니다.
아무래도 이미지 관련 작업이다보니까 Gdip을 사용해야할 것 같다는 느낌이 들지 않나요? 질문자분도 눈치 채셨는지 Gdip으로 어떻게 하냐고 질문을 주셨습니다. 답은 간단합니다. 두 이미지의 왼쪽 위부터, 오른쪽 아래까지 하나씩 일대일로 비교하면 되는거죠. 각 이미지를 A, B라고 했을 때 A(0,0)은 B(0,0)와, A(1,0)은 B(1,0)과... 그렇게 A(가로, 세로)와 B(가로, 세로)까지 일일이 비교하면 됩니다.
즉, 이미지의 컬러값(한 픽셀의 값)을 가져오는게 관건이지요. 그것도 "아주 빠르게" 말입니다.
먼저 Gdip 라이브러리에는 어떤 비트맵의 한 색을 가져오는 함수가 이미 존재하는데, Gdip_GetPixel()입니다. 비트맵과 좌표를 넣어주면 그 좌표의 정수값이 나옵니다. 그런데 이 방법은 느립니다. 그 이유를 전문 프로그래머가 아마 아닐 여러분께 쉽게 설명드리기는 어렵기 때문에 이 자리에서 설명드리진 않을 것입니다.
아무튼 모든 픽셀에 GetPixel()을 사용하는것은 미련한 짓이지요. 느리고 무겁습니다. 이를 빠르게 하기 위해 Gdip_LockBits()를 사용합니다. 이를 이용해서 비트맵 데이터에 직접 접근할 수 있습니다. 매번 복잡한 절차 없이 연결할 수 있는 핫라인을 만들어준다고 비유하면 쉬울까요?
Gdip_LockBits()에 대해서도 설명드리지 않을것입니다. 비트맵에 대한 정확한 이해가 필요합니다. 비트맵데이터 클래스는 무엇인지, Scan0은 무엇을 의미하는지, Stride는 무엇인지 등... 자세하게 공부하고 싶으시면 별도 검색을 통해 이해하시면 되오나, 간략하게 잘 정리되어있는 블로그를 찾아서 공유드립니다.
Using LockBits in GDI+
Understanding how to use LockBits is essential for creating high performance GDI+ applications. Usually, GDI+ is thought of as a low performance graphics API. While arguments can be made for this, …
supercomputingblog.com
[P] 디지털영상처리 - scan0와 stride, 배열접근에대한 이해
지난번 포스팅에서는 GDI+ 의 GetPixel, SetPixel 을 사용한 밝기조절을 알아봤다. 이 gdi+의 메소...
blog.naver.com
다시 본론으로 돌아가서, 우리는 아래와 같은 방법으로 데이터에 접근할 것입니다.
- Gdip_LockBits() 사용
- LockBits 상태에서 쓸 수 있는 전용 GetPixel() 함수로 픽셀 값 얻기
- Gdip_UnlockBits() 사용
LockBits 상태에서만 쓸 수 있는 각 픽셀에 빠르게 접근 가능한 함수를 이용한다는 점만 알아두세요.
예시 스크립트는 아래와 같습니다.
예전에 제가 비슷한 질문을 받고 만들어뒀던 Gdip_LockBits()와 LockBits 상태에서 빠르게 픽셀값을 얻을 수 있는 Gdip_GetLockBitPixel() 함수에 대한 예시입니다(두 함수는 모두 순정 Gdip_All.ahk에 포함되어있습니다.)
모든 픽셀을 탐색해서, RGB값 중 R 값이 B 값보다 높은 픽셀의 개수를 찾는 코드이지요. 돌려보시면 아시겠지만 정말 빠릅니다. (바탕화면의 1.png 사진을 이용하는 코드이며, Gdip_All.ahk가 lib 폴더 안에 있다는점 또한 주의해주세요. 제가 예제코드 정리를 해놓았을때 기준이라서 그렇습니다.)
우선 이 예시 스크립트를 이해하세요. 대충 감만 익히자고요. Scan0, Stride, BitmapData 변수는 Gdip_LockBits()의 출력변수로 나온 것입니다. 이해가 어려우시면 그냥 위 예시와 같은 형식을 외우세요.
Gdip_GetLockBitPixel(Scan0, X, Y, Stride)
Gdip_GetLockBitPixel()은 이렇게 씁니다. X, Y에 가져올 픽셀의 좌표를 쓰시면 됩니다. 0부터 시작하는 수입니다. Scan0에 메모리에 올라와있는 비트맵 데이터의 주소가 들어있으므로 이걸로 각 비트맵이 구분됩니다. Stride또한 비트맵마다 겹치면 안됩니다.
얼추 어떻게 사용하는지 이해하셨나요? Gdip_UnlockBits()를 통해 고정을 해제하는것 또한 중요합니다.
자, 이제 각 비트맵의 각 좌푯값을 빠르게 가져올 수 있는 방법을 알아냈습니다. 예시에선 가져온 픽셀값에서 R값과 B값을 비교하는 작업을 했지만, 질문자분은 그저 가져온 픽셀값끼리의 동일성만 비교하면 되지요. 응용 가능하실까요?
응용해보시는건 개인 연구나 공부로써 해보시면 좋습니다. 일단 저는 아래와 같이 만들어보았는데 테스트는 안해봤으나 혹시 오작동하면 잘 수정해서 쓰시면 되겠습니다.
이 예시는 바탕화면의 A.png와 B.png를 비교하는 코드가 되겠습니다. 말 그대로 두 비트맵의 각 좌표를 서로 비교하는 작업만 있을 뿐이지요.
그런데 함수를 이용하면 더 좋을 것 같군요. 아래와 같은 Gdip_CompareBitmap()함수를 만들어보았습니다. 두 비트맵을 입력받으면, 다른 부분이 있는 경우 그 좌표를 반환합니다.
두 비트맵을 매개변수로 받는 함수를 만들어보겠습니다.
이렇게 함수화를 해둬서 이를 Gdip_All.ahk의 적당한 위치에 다른 함수처럼 넣어주거나, 그냥 스크립트의 맨 밑에 박아두신 다음에
이런 식으로 깔끔하게 함수 하나로 쓸 수 있지요. 성공하면 DiffPos엔 배열(X, Y라는 키를 갖는 연관 배열)이 담길 것입니다.
아예 함수 하나로 만들어드렸으니 기타 본인에게 필요한 자잘한 부분은 이 함수를 수정해서 쓰시면 될 것 같습니다.
추가 질문이 있으실경우 별도의 질문글 남겨주세요.
혹여 배열을 모르실 경우 아래에 배열 강좌 또한 준비해두었으나, 본 답변에서 배열은 크게 중요하거나 어려운 개념이 아니기 때문에 그냥 반환값을 예시처럼 점 찍고 X, 점 찍고 Y(예: Arr라는 변수로 받았다면 Arr.X, Arr.Y)로 사용한다고만 알아두셔도 됩니다.
⚠ 이 강좌는 오토핫키 v1을 다룹니다
지금 보시는 강좌는 구버전 오토핫키(v1.1)를 다루고 있습니다. 따라서 본 강좌의 내용은 현재 최신 오토핫키 버전 (v2.0)과 호환되지 않습니다. 구버전의 정보가 필요한 것이 아니라면, 가능한 한 새로운 사이트에 작성한 v2 강좌(https://ahkv2.pnal.dev)를 봐주시길 바랍니다.