본문 바로가기

& 프로그래밍/& openCV

마우스로 선택한 부분만 출력 및 저장하기(polygon, ROI,openCV)

시중에 나온 openCV관련 서적은 ROI와 polygon, mouse event를 각 함수별로 예제를 다루고 있다

 

물론 각 함수별로 기초지식을 쌓아야 다양한 응용문제를 해결할 수 있겠지만, 기초에서 심화로 넘어가는 단계에선 살짝 막막한게 현실이다

 

이번에 내가 해본 실습은 다음과 같다

1. openCV를 이용해 이미지를 입력하고 윈도우에 띄운다
2. 윈도우로 띄운 이미지 중에 원하는 부분만 마우스로 클릭해서 polygon을 만든다
3. 선택된 polygon 내부의 이미지를 저장한다
4. polygon 좌표를 json파일로 저장한다

내가 작성한 코드를 순차적으로 풀이해보자면,

 

1. 모듈 임포트

이번 툴을 제작하는데 필요한 모듈은 아래와 같다.

import cv2 as cv
import imutils
import numpy as np
import json

2. 기본 구조

pts = [] #마우스로 클릭한 포인트를 저장
resultforJSON = [] # pts에 저장된 포인트를 json형태로 저장
file_path = './sample.json' #json으로 저장하기 위한 파일경로

3. ROI 함수 만들기

def draw_roi(event, x, y, flags, param): #roi검출을 위한 함수 정의
	img2 = img.copy()
    
    if event == cv.EVENT_LBUTTONDOWN: #마우스 왼쪽버튼을 클릭하면
    	pts.append((x,y)) #pts에 (x,y)좌표를 추가한다
        print('포인트 #%d 좌표값(%d,%d)' % (len(pts),x,y)) #정상적으로 추가되는지 출력으로 확인
    	
        resultforJSON.append({'point':[len(pts)],
        					  'coordinate':[[int(x),int(y)]]})
                              # 포인트 순서와 좌표값을 딕셔너리 형태로 추가해준다
                              
  	if event == cv.EVENT_RBUTTONDOWN: #마우스 오른쪽버튼을 클릭하면
    	pts.pop() #클릭했던 포인트를 삭제한다
        
   	if event == cv.EVENT_MBUTTONDOWN: #마우스 중앙(휠)버튼을 클릭하면
    	print('총 %d개의 포인트 설정' % len(pts))
        mask = np.zeros(img.shape, np.uint8) #컬러를 다루기 때문에 np로 형변환
        points = np.array(pts, np.int32)
        points = points.reshape((-1,1,2)) #pts 2차원을 이미지와 동일하게 3차원으로 재배열
        
        mask = cv.polylines(mask, [points], True, (255,255,255), 2) #포인트와 포인트를 연결하는 라인을 설정
        mask2 = cv.fillPloy(mask.copy(), [points], (255,255,255)) #폴리곤 내부 색상 설정
        
        ROI = cv.bitwise_and(mask2,img) # mask와 mask2에 중첩된 부분을 추출
        with open(file_path,'w') as outfile: #resultforJSON에 저장된 내용을 json파일로 추출
        	json.dump(resultforJSON,outfile,indent=4)
            
   		cv.imwrite('ROI.png',ROI)
        cv.imshow('ROI',ROI)
        cv.waitKey(0)
    
    if len(pts)>0: #포인트를 '원'으로 표시
    	cv.circle(img2,pts[-1],3,(0,0,255),-1)
    
    if len(pts)>1:
		for i in range(len(pts) -1):
        	cv.circle(img2, pts[i], 5,(0,0,255),-1)
            cv.line(img=img2, pt1=pts[i], pt2=pts[i+1], color=(255,0,0),thickness=2)
    cv.imshow('image',img2)

img= cv.imread('이미지 파일.jpg')
img = imutils.resize(img, width=500)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_roi)

while True:
	key = cv.waitKey(1) & 0xFF
    if key == 27:
    	break
    if key == ord('s'):
    	saved_data = {'ROI':pts}
        break
cv.destroyAllWindows()