본문 바로가기
데이터 과학 관련 스터디/OpenCV

[OpenCV] 이미지 관심영역 (ROI) 추출하기

by inhovation97 2022. 3. 11.
본 글은 파이썬으로 만드는 OpenCV프로젝트 서적의 내용을 포스팅하는 내용입니다. 
4.1장 관심영역

해당 실습은 cv2 라이브러리를 이용하여 진행합니다.

1. 마우스로 관심영역 추출하기
2. cv라이브러리로 간단히 하기

obj detection 모델에서는 이미지의 관심영역 ROI( Region of Interest )가 라벨이 됩니다. 

저는 roboflow라는 플랫폼에서 마우스로 드래그해가면서 이미지 데이터를 라벨링한 경험이 있는데, 이걸 직접 코딩해보는 실습입니다.

 

1. 마우스로 관심영역 추출하기

 

import cv2
import numpy as np

isDragging = False                         # 마우스 드래그 상태 저장
x0,y0,w,h = -1,-1,-1,-1                    # 영역 선택 좌표 저장
blue,red = (255,0,0),(0,0,255)              # 색상 값

def onMouse(event, x, y, flags, param):     # 마우스 이벤트 핸들 함수
    global isDragging, x0, y0, img          # 전역 변수 참조
    if event == cv2.EVENT_LBUTTONDOWN:      # 왼쪽 마우스 버튼 다운, 드래그 시작
        isDragging = True
        x0 = x
        y0 = y
        
    elif event == cv2.EVENT_MOUSEMOVE:      # 마우스 움직임
        if isDragging:                       # 드래그 진행 중
            img_draw = img.copy()            # 사각형 그림 표현을 위한 이미지 복제 (매번 같은 이미지에 그려지면 이미지가 더러워짐)
            cv2.rectangle(img_draw, (x0,y0), (x,y), blue, 2)  # 드래그 진행 영역 표시
            cv2.imshow('img', img_draw)       # 사각형으로 표시된 그림 화면 출력
            
    elif event == cv2.EVENT_LBUTTONUP:       # 왼쪽 마우스 버튼 업
        if isDragging:                        # 드래그 중지
            isDragging = False               
            w= x - x0                         # 드래그 영역 폭 계산
            h= y - y0                         # 드래그 영역 높이 계산
            print("x%d, y%d, w%d, h%d" % (x0, y0, w, h) )
            if w>0 and h>0:                  # 폭과 높이가 양수이면 드래그 방향이 옳음
                img_draw = img.copy()         # 선택 영역에 사각형 그림을 표시할 이미지 복제
                cv2.rectangle(img_draw, (x0, y0), (x, y), red, 2) # 선택 영역에 빨간색 사각형 표시
                cv2.imshow('img', img_draw)         # 빨간색 사각형이 그려진 이미지 화면 출력
                roi = img[y0:y0+h, x0:x0+w]         # 원본 이미지에서 선택 영역만 ROI로 지정
                cv2.imshow('cropped', roi)          # ROI 지정 영역을 새 창으로 표시
                cv2.moveWindow('cropped', 0,0)     # 새 창을 화면 좌측 상단으로 이동
                cv2.imwrite('../CV2/img/cropped.jpg', roi)  # ROI 영역만 파일로 저장
                print('cropped.')
            
            else:
            # 드래그 방향이 잘못된 경우 사각형 그림이 없는 원본 이미지 출력
                cv2.imshow('img', img)
                print('좌측 상단에서 우측 하단으로 영역을 드래그하세요.')

img = cv2.imread('../CV2/img/sunset.jpg')
cv2.imshow('img', img)
cv2.setMouseCallback('img', onMouse) # 마우스 이벤트 등록
cv2.waitKey()
cv2.destroyAllWindows()

 

 

코드가 좀 길지만 어렵진 않습니다. 

마우스 이벤트로 작업을 진행하는 실습은 이전에 진행했었습니다. 

우선 보통 컴퓨터 비전을 공부하면 HWC 순서에 익숙하지만, numpy 배열이라서 w,h 순서로 데이터를 핸들링 해주어야 합니다.

 

위 코드는 크게 3가지 이벤트로 진행됩니다. 

onMouse 첫 if문부터 봅시다.

마우스 왼쪽 버튼이 눌리면, isDragging 상태를 바꿔주고 클릭한 좌표를 기억합니다. 이 좌표는 관심 영역의 좌상단이어야합니다.

그다음 마우스가 눌린 상태로 움직이면, 이미지를 복제해줍니다. (계속 같은 이미지에 그리면 box가 중첩돼 더러워짐)

복제한 이미지에 ROI를 그려줍니다. 

마우스 왼쪽 버튼을 떼면, isDragging 상태를 다시 바꿔주고 클릭했을 때의 좌표와 뗏을 때의 좌표를 계산하여 폭과 높이를 구해줍니다. 박스 친 ROI를 따로 저장해주고 이미지 창도 띄우게 됩니다.

 

결과
저장된 ROI

지정 경로에 가면 드래그한 ROI 이미지까지 저장 돼있는 것을 알 수 있습니다.

좌상단에서 우하단으로 드래그하지 않으면, 제대로하라는 메시지가 뜰겁니다.

 

1. cv2라이브러리로 간단히 하기

 

import cv2, numpy as np

img = cv2.imread('../CV2/img/sunset.jpg')

x,y,w,h = cv2.selectROI('img', img, False)
if w and h:
    roi = img[y:y+h, x:x+w]
    cv2.imshow('cropped', roi)                   # ROI 지정 영역을 새창으로 표시
    cv2.moveWindow('cropped', 0, 0)              # 새창을 화면 측 상단으로 이동
    cv2.imwrite('../CV2/img/cropped2.jpg', roi)  # ROI 영역만 파일로 저장
    
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

코드가 훨씬 간결합니다. 

 

cv2.selectROI( [win_name,], img[, showCrossHair=True, fromCenter=Flase] ) - ROI 지정

입력 : win_name - ROI 선택을 진행할 창의 이름, str

        ing - ROI 선택을 진행할 이미지, numpy ndarray

        showCrissHair - 선택 영역 중심에 십자 모양 표시 여부

        fromCenter - 마우스 시작 지점을 영역의 중심으로 지정

        ret - 선택한 영역 좌표와 크기( x, y, w, h ), 선택을 취소한 경우 모두 0

결과

해당 코드는 ROI를 마우스로 드래그한 뒤 엔터나 스페이스 바를 누르면, 지정한 경로로 크롭된 이미지가 저장되고,

c키를 누르면 종료됩니다. 

 

댓글