본문 바로가기

머신러닝,딥러닝/opencv

opencv 입문하기 5편 마우스 이벤트(2)

지난 번에 하던 마우스 이벤트의 예제를 조금 더 해보겠습니다. 

 

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

 

[Python] OpenCv를 이용하여 마우스 위치 가져오기

프로그래밍과 재테크에 관심 많은 프로그래머 블로그

tayrelgrin.blogspot.com

https://mathbang.net/454

 

원의 방정식, 원의 방정식 표준형

원의 방정식은 그리 어려운 내용이 아니에요. 간단하게 두 점 사이의 거리를 이용해서 구할 수 있으니까요. 원과 관련된 기본적인 용어의 정의와 특징만 이해하고 있으면 돼요. 오히려 중학교 �

mathbang.net