영상처리 (Open CV) - 이미지 관심 영역(ROI) 추출하기.

2024. 8. 8. 16:56---포트폴리오---/개인 프로젝트 or 작업물

728x90

< 설계 >

1. 이미지의 관심 영역을 설정하고 추출하는 기능을 구현.

2. 입력 영상은 컬러 or 흑백 사진 사용.

3. 이미지 관심영역이란 임의로 도형을 드래그 또는 포인트 점을 찍어 만든 영역을 말함.


3-1. 타원

  • 마우스 왼쪽 버튼을 눌러 드래그 하여 영역 설정.
  • 드래그하는 동안 타원 그림이 그려져야 함.

3-2. 다각형 (포인트 사용)

  • 여러 개의 점을 찍어 다각형 영역을 설정.
  • 최소 3개의 점이 찍혀야 함.
  • 점과 점 사이에는 선이 그려져야함
  • 다각형을 유지하기 위해서는 내각이 180도 이하여야 함.
  • 임의로 클릭하여 점을 찍을 때, 첫 번째 점과의 거리가 20px 이하여 다각형을 닫음. (마지막으로 찍은 점은 무시.)

4. 타원 또는 다각형 형태로 설정한 영역 내부의 이미지를 추출 후 저장.

5. 타원, 다각형을 할 수 있는 모드는 키보드를 눌러 선택.

  • 처음 시작은 NULL 모드.
  • 타원 모드는 키보드 숫자 '1'을 눌러 변경.
    다시 누르면 'NULL' 모드로 변경.
  • 다각형 모드는 키보드 숫자 '2'를 눌러 변경.
    마찬가지로 다시 누르면 'NULL' 모드로 변경.

< 작성 코드 >

import cv2
import numpy as np

drawing = False
mode = 'NULL'
ix, iy = -1, -1
img_count = 1
Polygon_points = []

def onMouse(event, x, y, flags, param):
    global ix, iy, drawing, mode, image, Polygon_points, img_count

    if event == cv2.EVENT_LBUTTONDOWN:
        if mode == 'Ellipse':  
            drawing = True
            ix, iy = x, y  

        elif mode == 'Polygon':
            if len(Polygon_points) > 0 and np.linalg.norm(np.array(Polygon_points[0]) - np.array([x,y])) < 20:
                Polygon_points.append(Polygon_points[0])
                cv2.polylines(image, [np.array(Polygon_points)], isClosed=True, color=(0,255,0),thickness= 2)
                save_roi_polygon()
                Polygon_points.clear()
            else:
                Polygon_points.append((x,y))
                if len(Polygon_points) > 1:
                    cv2.line(image, Polygon_points[-2], Polygon_points[-1], (0,255,0),2)
                cv2.circle(image, (x,y),3,(0,0,255),-1)
            cv2.imshow('image',image)

    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing and mode == 'Ellipse':
            temp_image = image.copy() 
            radius = int(np.sqrt((x - ix)**2 + (y - iy)**2))  
            cv2.circle(temp_image, (ix, iy), radius, (255, 0, 0), 2) 
            cv2.imshow('image', temp_image)

    elif event == cv2.EVENT_LBUTTONUP:
        if drawing and mode == 'Ellipse': 
            drawing = False
            radius = int(np.sqrt((x - ix)**2 + (y - iy)**2)) 
            cv2.circle(image, (ix, iy), radius, (0, 255, 0), 2) 
            save_roi_ellipse(ix, iy, radius)  
            cv2.imshow('image', image)

def save_roi_ellipse(ix, iy, radius):
    global img_count
    x1, y1 = max(0, ix - radius), max(0, iy - radius)
    x2, y2 = min(image.shape[1], ix + radius), min(image.shape[0], iy + radius)
    roi = image[y1:y2, x1:x2].copy()
    cv2.imwrite(f'roi_{img_count:04d}.jpg', roi)
    img_count += 1

def save_roi_polygon():
    global img_count
    mask = np.zeros_like(image)
    cv2.fillPoly(mask, [np.array(Polygon_points)], (255, 255, 255))
    masked_image = cv2.bitwise_and(image, mask)
    x, y, w, h = cv2.boundingRect(np.array(Polygon_points))
    roi = masked_image[y:y + h, x:x + w].copy()
    cv2.imwrite(f'roi_{img_count:04d}.jpg', roi)
    img_count += 1

def switch(key):
    global mode, Polygon_points
    if key == ord('1'):
        mode = 'Ellipse' if mode != 'Ellipse' else 'NULL'

    elif key == ord('2'):
        mode = 'Polygon' if mode != 'Polygon' else 'NULL'
        if mode == 'NULL' and len(Polygon_points) > 2:
            cv2.polylines(image, [np.array(Polygon_points)], isClosed=True, color=(0,255,0),thickness=2)
            Polygon_points.clear()

    print(f'Mode switched to: {mode}')

image = cv2.imread('old.jpg')
cv2.imshow('image', image)
cv2.setMouseCallback('image', onMouse)

while True:
    key = cv2.waitKey(1) & 0xFF
    if key == 27:
        break
    switch(key)

cv2.destroyAllWindows()

 

<코드 설명>

1. 원을 그리는 모드일 때 그리는 상태로 설정하고, 마우스 클릭 지점 저장.

 

2. 다각형 그리기 모드일 때 첫 번째 점과 가까운 위치를 클릭하면 다각형을 닫고, 닫힌 다각형을 의해 시작점을 추가함.
그리고 해당 다각형을 저장하고 새로운 다각형을 그리기 위해 다각형 점 리스트를 초기화 시킴.

 

2-1. 새로운 점을 추가하고 이전의 점과 현재 그린 점을 연결하는 선 그리고 클릭한 위치에 작은 원을 그려 점을 표시 후 업데이트 된 이미지를 표시함.

 

3. 타원 모드에서 드래그하여 마우스를 움직일 때 원본 이미지를 복사해 임시 이미지를 만들고 반지름을 계산 후 임시 이미지에 원을 그리고 표시함.

 

3-1. 마우스 왼쪽 버튼을 뗐을 때 그리는 상태를 해제하고 최종적으로 반지름을 계산 후 원본 이미지에 최종 원을 그린 다음 저장하고 표시함.

 

4. 타원 ROI를 저장하는 부분은 타원의 바운딩 박스를 계산하여 영역을 설정하고 타원 내부 영역을 추출 후 이미지 파일로 저장. 그리고 여러 장의 사진을 저장할 수 있으니 저장할 이미지의 번호를 증가 시킴.

 

5. 다각형도 위와 마찬가지로 동작함.

 

6. 모드를 전환하기 위해 switch 함수를 사용하여 '1'을 눌렀을 때 타원 모드로 전환 다시 누르면 'NULL'로 전환

'2'를 누르면 다각형 모드로 전환.

 

7. 아스키코드를 이용해 메인 루프 동작 여부 결정. ESC(27) 을 누르면 프로그램 종료. (아닐 경우 모드 전환 함수 호출)

< 동작 사진>

1. 기본 동작 상태 (NULL)

 

2. 키보드 '1'을 눌렀을 때 (Ellipse)

 

3. 키보드 '2'를 눌렀을 때 (Polygon)

 

4. 타원 그리기 모드

 

5. 다각형 그리기 모드

 

6. 종료 후 저장된 사진

728x90