지난 번에 하던 마우스 이벤트의 예제를 조금 더 해보겠습니다.
1) 마우스가 지나간 길을 따라 선을 그려주는 프로그램
선이 뭔가 투박해서 보기가 안 좋습니다.
일단 위 코드는 아래 와같습니다.
# 마우스가 지나는 길을 따라 선을 그려주는 프로그램
# 이 때, 선을 그리다 보면 뭔가 끊어지는 듯한 느낌이 있다는 이슈가 생김.
import cv2
import numpy as np
def mouse_fn(event, x, y, flags, param):
global img
if event == cv2.EVENT_LBUTTONDOWN:
print('EVENT_LBUTTONDOWN: %d, %d' % (x, y))
elif event == cv2.EVENT_LBUTTONUP:
print('EVENT_LBUTTONUP: %d, %d' % (x, y))
elif event == cv2.EVENT_MOUSEMOVE:
# print('EVENT_FLAG_LBUTTON: %d, %d' % (x, y)) # 커서 움직이는 모든 위치의 실시간으로 좌표 보여줌
if flags & cv2.EVENT_FLAG_LBUTTON: # nd 연산이기에 == 가 아닌 & 로 해주세요.
cv2.circle(img, (x, y), 5, (0, 0, 255), -1) # -1 은 속을 채운다는 의미
cv2.imshow('img', img)
# 도화지 만들기
img = np.ones((480, 640, 3), dtype=np.uint8) * 255
cv2.namedWindow('img')
cv2.setMouseCallback('img', mouse_fn, img)
cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()
투박한 선을 조금 부드럽게 나오도록 코드를 수정해보겠습니다.
1-2) 코드 수정
조금 개선이 된 것을 느낄 수 있습니다.
# 마우스가 지나간 길을 따라 선을 그려주는 프로그램 _ 개량
import cv2
import numpy as np
oldx = oldy = -1
def mouse_fn(event, x, y, flags, param):
global img, oldx, oldy
if event == cv2.EVENT_LBUTTONDOWN:
oldx, oldy = x, y # 이전 좌표를 받기
print('EVENT_LBUTTONDOWN: %d, %d' % (x, y))
elif event == cv2.EVENT_LBUTTONUP:
print('EVENT_LBUTTONUP: %d, %d' % (x, y))
elif event == cv2.EVENT_MOUSEMOVE:
if flags & cv2.EVENT_FLAG_LBUTTON:
# Line_aa 옵션 넣어주면 좀 더 부드럽게 그려진다.
cv2.line(img, (oldx, oldy), (x, y), (0, 0, 255), 4, cv2.LINE_AA)
cv2.imshow('img', img)
# 마우스 커서 이동한 위치를 저장
oldx, oldy = x, y # 이동한 점을 다시 받아주기
# 도화지 만들기
img = np.ones((480, 640, 3), dtype=np.uint8) * 255
cv2.namedWindow('img')
cv2.setMouseCallback('img', mouse_fn, img)
cv2.imshow('img', img)
cv2.waitKey()
cv2.destroyAllWindows()
앞의 코드와 달라진 것은 좌표 부분입니다.
기본 좌표 마우스 커서를 움직이고 나서 멈춘 좌표를 다시 이전 좌표로 저장하고, LINE_AA 옵션을 추가하였습니다.
+) 마우스 커서 좌표에 대한 설명
코드들을 보다보면 마우스 커서의 현재 위치에 대한 변수가 자동으로 할당된다는 느낌을 받을 수 있습니다.
윈도우 창에서 커서를 움직이다 보면 윈도우 창 하단 바에 x,y 좌표가 나타나는 것을 볼 수 있는데, 이 부분이 자동으로 x,y값을 넣어주면 마우스 이벤트에서는 할당되는 듯 보입니다.
테스트를 해보죠.
import cv2 as cv # OpenCV import
import numpy as np # 행렬(img)를 만들기 위한 np import
# 마우스 이벤트 콜백함수 정의
def mouse_callback(event, x, y, flags, param):
print("마우스 이벤트 발생, x:", x, " y:", y) # 이벤트 발생한 마우스 위치 출력
img = np.zeros((256, 256, 3), np.uint8) # 행렬 생성, (가로, 세로, 채널(rgb)),bit)
cv.namedWindow('image') # 마우스 이벤트 영역 윈도우 생성
cv.setMouseCallback('image', mouse_callback)
while(True):
cv.imshow('image', img)
k = cv.waitKey(1) & 0xFF
if k == 27: # ESC 키 눌러졌을 경우 종료
print("ESC 키 눌러짐")
break
cv.destroyAllWindows()
결과와 코드를 함께 봐주세요.
x,y 좌표가 기본으로 할당되면서 출력되는 것을 위에서 확인할 수 있습니다.
2) 마우스 왼쪽 버튼을 누른 자리와, 뗀 자리를 대각선으로 하는 직사각형을 그리는 프로그램
(+ 모드를 변경해서 원도 그리게 하는 기능까지 추가)
-마우스 왼쪽 버튼을 누른 상태에서 커서를 사선으로 움직인 뒤 왼쪽 버튼에서 손을 떼었을 때 아래의 결과가 보여집니다.
import numpy as np
import cv2
from random import shuffle
import math
mode, drawing = True, False
xi, yi = -1, -1
B = [i for i in range(256)]
G = [i for i in range(256)]
R = [i for i in range(256)]
def onMouse(event, x, y, flags, frame):
global xi, yi, drawing, mode, B, G, R
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
xi, yi = x, y
shuffle(B), shuffle(G), shuffle(R)
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
if mode:
cv2.rectangle(frame, (xi, yi), (x, y), (B[0], G[0], R[0]), -1)
else:
r = (xi - x)**2 + (yi-y)**2
r = int(math.sqrt(r))
cv2.circle(frame, (xi, yi), r, (B[0], G[0], R[0]), -1)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
if mode:
cv2.rectangle(frame, (xi, yi), (x, y), (B[0], G[0], R[0]), -1)
else:
r = (xi - x)**2 + (yi-y)**2
r = int(math.sqrt(r))
cv2.circle(frame, (xi, yi), r, (B[0], G[0], R[0]), -1)
frame = np.zeros((512, 512, 3), np.uint8)
cv2.namedWindow('frame')
cv2.setMouseCallback('frame', onMouse, param=frame)
while True:
cv2.imshow('frame', frame)
key = cv2.waitKey(1)
if key == 27:
break
elif key == ord('m'):
mode = not mode
cv2.destroyAllWindows()
마우스 커서 좌표 생성에 대한 이해가 됐다면 위에서 최초의 위치 좌표를 -1 이라고 했을 때 마우스 왼쪽 버튼 클릭을 하였을 때, 이 부분이 출발점이고 elif 조건에 따라서 x,y 는 기본적인 커서의 위치가 되므로 출발점과 목적점을 잡아서 사각형과 원이 생성됨을 알 수 있습니다.
위 코드에서는 m 버튼을 누를 시 mode 값을 false 로 변경하여 원이 그려지도록 하였습니다.
원을 그릴 때에는 반지름이 필요하기에 이를 좌표 출발점과 목적점을 이용해서 x,y 변화량을 제곱하는데 여기에 root를 씌우면 반지름이 되는 걸 구현하였습니다.
이상 마우스 이벤트 관련 포스팅을 마치겠습니다 :)
* 참고 *
tayrelgrin.blogspot.com/2018/11/python.html
'머신러닝,딥러닝 > opencv' 카테고리의 다른 글
opencv 입문하기 7-1편 histogram stretching, equalization (0) | 2020.08.08 |
---|---|
opencv 입문하기 6편 트랙바, 산술 연산 (0) | 2020.07.26 |
opencv 입문하기 4편 키보드, 마우스 이벤트(1) (0) | 2020.07.25 |
opencv 입문하기 3편 영상생성,추출, 그리기 함수 (0) | 2020.07.21 |
opencv 입문하기 2편 - 이미지 객체 속성 , 픽셀값 변경 (0) | 2020.07.20 |