13장. Shape 분석과 ROI 처리
ROI 안에서만 처리하기
벽에 페인트를 칠할 때 창틀에 종이테이프를 붙여 두면 칠하고 싶은 벽에만 페인트가 묻습니다. 사진도 필요한 곳에만 처리를 걸고 싶을 때가 많습니다. 송장의 전화번호만 흐리게 가리거나, 송장 부분만 흑백 두 값으로 또렷하게 만드는 일입니다. ROI 를 잘라 처리한 뒤 원본의 같은 자리에 다시 붙이면 됩니다.
그림의 네 단계가 ROI 안에서만 처리하는 순서입니다. 셋째 단계에서 슬라이싱에 대입(img[y0:y1, x0:x1] = 처리한조각)하면 그 자리만 바뀌고, 넷째 단계에서 ROI 밖의 칸이 그대로인지 확인합니다. 여기서 종이테이프를 붙이지 않은 곳이 ROI, 페인트가 블러나 이진화 같은 처리입니다.
가운데는 전화번호 줄만 Gaussian 블러로 흐리게 해 읽을 수 없게 만든 사진이고, 오른쪽은 송장 부분만 Otsu 이진화(8장)로 흑백 두 값으로 만든 뒤 컬러 그림에 다시 붙인 사진입니다. 두 사진 모두 상자와 스티커는 원본 그대로입니다.
붙여 넣을 때 맞춰야 할 것
| 맞출 것 | 까닭 | 맞추는 법 |
|---|---|---|
| 크기 | 다르면 대입에서 오류 | 처리 함수가 크기를 바꾸지 않게, 바꿨으면 cv2.resize |
| 채널 수 | 흑백 조각(2차원)은 컬러 자리(3채널)에 바로 못 넣는다 | cv2.cvtColor(조각, cv2.COLOR_GRAY2BGR) |
| 자료형 | 소수 조각은 uint8 자리에서 잘린다 | astype(np.uint8) 이나 np.clip |
사람이 직접 고르는 ROI
좌표를 모를 때는 사람이 마우스로 네모를 그려 고를 수 있습니다. cv2.selectROI 는 창에 그림을 띄우고, 마우스로 네모를 그린 뒤 Enter(또는 Space)를 누르면 (x, y, 가로, 세로) 를 돌려줍니다. 창이 필요해서 교안의 실행 결과에는 넣지 않았고, 아래는 실행하지 않은 안내 코드입니다. 내 PC 의 VS Code 에서 파이썬 파일로 실행해 보세요.
import cv2
img = cv2.imread("ch13_parcel.png")
# 창에 그림을 띄우고 마우스로 네모를 그린다. Enter 나 Space 로 확정, c 로 취소
x, y, w, h = cv2.selectROI("고르기", img, showCrosshair=True)
cv2.destroyAllWindows()
print("고른 자리:", x, y, w, h)
roi = img[y:y + h, x:x + w].copy()짧은 예시
import cv2
import numpy as np
img = cv2.imread("ch13_parcel.png")
before = img.copy() # 견주려고 원본을 따로 둔다
# 전화번호 줄의 자리: 세로 88~107, 가로 64~189
y0, y1, x0, x1 = 88, 108, 64, 190
roi = img[y0:y1, x0:x1]
# ROI 만 크게 흐린다. 크기는 그대로라 같은 자리에 바로 붙일 수 있다
img[y0:y1, x0:x1] = cv2.GaussianBlur(roi, (15, 15), 0)
# 바뀐 칸이 모두 ROI 안에 있는지 확인한다
changed = np.any(img != before, axis=2) # 칸마다 세 숫자 중 하나라도 다르면 True
ys, xs = np.nonzero(changed) # 바뀐 칸의 세로, 가로 번호
print(int(ys.min()) >= y0, int(ys.max()) < y1, int(xs.min()) >= x0, int(xs.max()) < x1)
# 결과: True True True True → 바뀐 칸은 모두 ROI 안np.nonzero 는 0 이 아닌(True 인) 칸의 번호를 돌려줍니다. 바뀐 칸의 가장 작은 번호와 가장 큰 번호가 ROI 의 범위 안이면 바깥은 그대로라는 뜻입니다.
주의할 점
- 블러처럼 이웃 칸을 섞는 처리는 ROI 의 가장자리에서 ROI 밖의 칸을 보지 못합니다. 그래서 ROI 경계에 흐린 정도가 갑자기 바뀌는 선이 보일 수 있습니다. 자연스럽게 하려면 여백을 붙여 처리한 뒤 가운데만 붙입니다.
roi = img[...]는 창이라roi[:] = 처리결과로 써도 원본이 바뀝니다. 그러나roi = cv2.GaussianBlur(roi, ...)는 roi 라는 이름에 새 그림을 붙일 뿐 원본을 바꾸지 않습니다. 붙여 넣기는img[y0:y1, x0:x1] = ...처럼 원본 쪽에 대입합니다.