์ด๋ฏธ์ง ๊ด์ฌ์์ญ(ROI)
๊ด์ฌ์์ญ(Region of Interest, ROI)์ ์ด๋ฏธ์ง ๋ด์์ ํน๋ณํ ์ฃผ๋ชฉํ๊ณ ์ถ์ ์์ญ์ ์๋ฏธํฉ๋๋ค.
ROI ๊ธฐ๋ณธ ๊ฐ๋
๊ด์ฌ์์ญ์ ์ปดํจํฐ ๋น์ ์์ ์ ์ฒด ์ด๋ฏธ์ง ์ค ํน์ ๋ถ๋ถ๋ง์ ๋ถ์ํ๊ณ ์ ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์ผ๋ชฐ ์ฌ์ง์์ ํ์ ๋ถ๋ถ๋ง์ ์ถ์ถํ๊ฑฐ๋, ์ผ๊ตด ์ฌ์ง์์ ๋ ์์ญ๋ง์ ๋ถ์ํ๋ ๊ฒฝ์ฐ์ ํ์ฉ๋ฉ๋๋ค.
ROI์ ์ฅ์
-
์ฒ๋ฆฌ ์๋ ํฅ์: ์ ์ฒด ์ด๋ฏธ์ง ๋์ ํ์ํ ๋ถ๋ถ๋ง ์ฒ๋ฆฌ
-
๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ: ์์ ์์ญ๋ง ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋
-
์ ํ๋ ๊ฐ์ : ๋ถํ์ํ ๋ฐฐ๊ฒฝ ์ ๋ณด ์ ๊ฑฐ
์ขํ๋ฅผ ์ด์ฉํ ROI ์ง์
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ์ ์ง์ ์ขํ์ ํฌ๊ธฐ๋ฅผ ์ง์ ํ๋ ๊ฒ์ ๋๋ค.
๊ธฐ๋ณธ ROI ์ง์ ๋ฐฉ๋ฒ
import cv2
import numpy as np
# ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
img = cv2.imread('./img/sunset.jpg')
# ROI ์ขํ ๋ฐ ํฌ๊ธฐ ์ค์
x = 320 # ์์ x ์ขํ
y = 150 # ์์ y ์ขํ
w = 50 # ๋๋น
h = 50 # ๋์ด
# numpy ์ฌ๋ผ์ด์ฑ์ ์ด์ฉํ ROI ์ง์
roi = img[y:y+h, x:x+w]
print(roi.shape) # (50, 50, 3)
# ROI ์์ญ์ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
cv2.rectangle(roi, (0,0), (h-1, w-1), (0,255,0))
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
ํต์ฌ ํฌ์ธํธ:
-
img[y:y+h, x:x+w]: numpy ๋ฐฐ์ด ์ฌ๋ผ์ด์ฑ์ผ๋ก ROI ์ถ์ถ -
cv2.rectangle(): ์ง์ ๋ ์์ญ์ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ -
RGB ๊ฐ
(0,255,0): ๋ น์์ผ๋ก ROI ์์ญ ํ์
ROI ๋ณต์ ๋ฐ ์กฐ์
import cv2
import numpy as np
img = cv2.imread('../img/sunset.jpg')
x=320; y=150; w=50; h=50
# ROI ์ง์ ๋ฐ ๋ณต์
roi = img[y:y+h, x:x+w]
img2 = roi.copy() # ROI ๋ฐฐ์ด ๋ณต์
# ์๋ณธ ์ด๋ฏธ์ง์ ROI ์ถ๊ฐ (ํ์์ ๋ ๊ฐ๋ก ๋ง๋ค๊ธฐ)
img[y:y+h, x+w:x+w+w] = roi
# ๋ ๊ฐ์ ํ์ ์์ญ์ ์ฌ๊ฐํ ํ์
cv2.rectangle(img, (x,y), (x+w+w, y+h), (0,255,0))
cv2.imshow("img", img) # ์๋ณธ ์ด๋ฏธ์ง ์ถ๋ ฅ
cv2.imshow("roi", img2) # ROI๋ง ๋ณ๋ ์ถ๋ ฅ
cv2.waitKey(0)
cv2.destroyAllWindows()
๋ง์ฐ์ค ๋๋๊ทธ๋ก ROI ์ง์
์ขํ๋ฅผ ์ง์ ์ ๋ ฅํ๋ ๋์ ๋ง์ฐ์ค ๋๋๊ทธ๋ฅผ ์ด์ฉํ๋ฉด ๋์ฑ ์ง๊ด์ ์ผ๋ก ROI๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
๋ง์ฐ์ค ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฐฉ์
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, y0 = x, 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, h = x - x0, y - y0
print(f"x:{x0}, y:{y0}, w:{w}, h:{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 ์ง์ ๋ฐ ํ์
roi = img[y0:y0+h, x0:x0+w]
cv2.imshow('cropped', roi)
cv2.moveWindow('cropped', 0, 0)
cv2.imwrite('./cropped.jpg', roi)
print("cropped.")
else:
cv2.imshow('img', img)
print("์ข์ธก ์๋จ์์ ์ฐ์ธก ํ๋จ์ผ๋ก ๋๋๊ทธํ์ธ์.")
# ๋ฉ์ธ ์คํ ์ฝ๋
img = cv2.imread('../img/sunset.jpg')
cv2.imshow('img', img)
cv2.setMouseCallback('img', onMouse) # ๋ง์ฐ์ค ์ด๋ฒคํธ ๋ฑ๋ก
cv2.waitKey()
cv2.destroyAllWindows()
๋ง์ฐ์ค ์ด๋ฒคํธ ์ฒ๋ฆฌ ๊ณผ์ :
-
๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ ๋ค์ด: ๋๋๊ทธ ์์์ ์ ์ฅ
-
๋ง์ฐ์ค ์์ง์: ์ค์๊ฐ์ผ๋ก ์ ํ ์์ญ ํ์
-
๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ ์ : ROI ํ์ ๋ฐ ์ ์ฅ
cv2.selectROI() ํจ์ ํ์ฉ
OpenCV์์ ์ ๊ณตํ๋ ๋ด์ฅ ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณต์กํ ๋ง์ฐ์ค ์ด๋ฒคํธ ์ฒ๋ฆฌ ์์ด๋ ์ฝ๊ฒ ROI๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
selectROI ํจ์ ๋ฌธ๋ฒ
ret = cv2.selectROI(win_name, img, showCrossHair=True, fromCenter=False)
๋งค๊ฐ๋ณ์ ์ค๋ช :
|
๋งค๊ฐ๋ณ์ |
ํ์ |
์ค๋ช |
|---|---|---|
|
win_name |
str |
๊ด์ฌ์์ญ์ ํ์ํ ์ฐฝ์ ์ด๋ฆ |
|
img |
numpy.ndarray |
๊ด์ฌ์์ญ์ ํ์ํ ์ด๋ฏธ์ง |
|
showCrossHair |
bool |
์ ํ ์์ญ ์ค์ฌ์ ์ญ์ ๋ชจ์ ํ์ ์ฌ๋ถ |
|
fromCenter |
bool |
๋ง์ฐ์ค ์์ ์ง์ ์ ์์ญ์ ์ค์ฌ์ผ๋ก ์ง์ |
๋ฐํ๊ฐ:
-
ret: ์ ํํ ์์ญ์ ์ขํ์ ํฌ๊ธฐ (x, y, w, h) -
์ ํ์ ์ทจ์ํ๋ฉด ๋ชจ๋ ๊ฐ์ด 0์ผ๋ก ๋ฐํ
selectROI ์ฌ์ฉ ์์
import cv2
import numpy as np
img = cv2.imread('../img/sunset.jpg')
# ROI ์ ํ (๋ง์ฐ์ค ๋๋๊ทธ ํ ์คํ์ด์ค/์ํฐ๋ก ํ์ , 'c'๋ก ์ทจ์)
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)
cv2.moveWindow('cropped', 0, 0)
cv2.imwrite('./cropped2.jpg', roi)
cv2.waitKey(0)
cv2.destroyAllWindows()
selectROI ์ฌ์ฉ๋ฒ:
-
๋ง์ฐ์ค๋ก ์ํ๋ ์์ญ ๋๋๊ทธ
-
์คํ์ด์ค๋ฐ ๋๋ ์ํฐํค๋ก ์ ํ ํ์
-
'c' ํค๋ก ์ ํ ์ทจ์
-
ESC ํค๋ก ์ ์ฒด ์ข ๋ฃ
์ค๋ฌด ํ์ฉ ํ
ROI ์ฒ๋ฆฌ ์ต์ ํ
# ํฐ ์ด๋ฏธ์ง์์ ์์ ROI๋ง ์ฒ๋ฆฌํ์ฌ ์ฑ๋ฅ ํฅ์
def process_roi_only(img, x, y, w, h):
roi = img[y:y+h, x:x+w]
# ROI์๋ง ๋ฌด๊ฑฐ์ด ์ฐ์ฐ ์ ์ฉ
processed_roi = expensive_operation(roi)
# ๊ฒฐ๊ณผ๋ฅผ ์๋ณธ ์ด๋ฏธ์ง์ ๋ค์ ์ฝ์
result = img.copy()
result[y:y+h, x:x+w] = processed_roi
return result
๋ค์ค ROI ์ฒ๋ฆฌ
# ์ฌ๋ฌ ROI๋ฅผ ๋์์ ์ฒ๋ฆฌ
def process_multiple_rois(img, roi_list):
results = []
for x, y, w, h in roi_list:
roi = img[y:y+h, x:x+w]
processed = some_processing(roi)
results.append(processed)
return results
# ์ฌ์ฉ ์์
roi_coordinates = [(100,100,50,50), (200,150,60,40), (300,200,80,70)]
processed_rois = process_multiple_rois(img, roi_coordinates)
๋์ ROI ์ถ์
# ๊ฐ์ฒด ์ถ์ ๊ณผ ์ฐ๋ํ ๋์ ROI
def track_and_process(video_path):
cap = cv2.VideoCapture(video_path)
# ์ฒซ ํ๋ ์์์ ROI ์ ํ
ret, frame = cap.read()
x, y, w, h = cv2.selectROI('Select Object', frame, False)
while True:
ret, frame = cap.read()
if not ret:
break
# ํ์ฌ ํ๋ ์์ ROI ์ฒ๋ฆฌ
roi = frame[y:y+h, x:x+w]
processed_roi = apply_filter(roi)
# ๊ฒฐ๊ณผ ํ์
cv2.imshow('ROI', processed_roi)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
์ฃผ์์ฌํญ ๋ฐ ํ
๊ฒฝ๊ณ๊ฐ ์ฒ๋ฆฌ
def safe_roi_extraction(img, x, y, w, h):
"""์์ ํ ROI ์ถ์ถ (๊ฒฝ๊ณ๊ฐ ์ฒดํฌ ํฌํจ)"""
height, width = img.shape[:2]
# ๊ฒฝ๊ณ๊ฐ ๋ณด์
x = max(0, min(x, width-1))
y = max(0, min(y, height-1))
w = min(w, width - x)
h = min(h, height - y)
if w <= 0 or h <= 0:
return None
return img[y:y+h, x:x+w]
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
ROI๋ฅผ ๋ง์ด ์์ฑํ ๋๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ฃผ์ํ์ธ์.
roi.copy()๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋ฆฝ์ ์ธ ๋ฐฐ์ด์ ๋ง๋ค๊ฑฐ๋,
์ฐธ์กฐ๋ง ์ฌ์ฉํ ์ง ์ ์คํ ๊ฒฐ์ ํด์ผ ํฉ๋๋ค.
์ฑ๋ฅ ๊ณ ๋ ค์ฌํญ
-
์์ ROI: ์ฒ๋ฆฌ ์๋ ํฅ์, ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ
-
ํฐ ROI: ๋ ๋ง์ ์ ๋ณด ๋ณด์กด, ๋์ ์ ํ๋
-
์ ์ ํ ๊ท ํ์ ์ฐพ๊ธฐ๊ฐ ์ค์