๐Ÿ˜Roi

์ด๋ฏธ์ง€ ๊ด€์‹ฌ์˜์—ญ(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()

๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๊ณผ์ •:

  1. ๋งˆ์šฐ์Šค ์™ผ์ชฝ ๋ฒ„ํŠผ ๋‹ค์šด: ๋“œ๋ž˜๊ทธ ์‹œ์ž‘์  ์ €์žฅ

  2. ๋งˆ์šฐ์Šค ์›€์ง์ž„: ์‹ค์‹œ๊ฐ„์œผ๋กœ ์„ ํƒ ์˜์—ญ ํ‘œ์‹œ

  3. ๋งˆ์šฐ์Šค ์™ผ์ชฝ ๋ฒ„ํŠผ ์—…: ROI ํ™•์ • ๋ฐ ์ €์žฅ


cv2.selectROI() ํ•จ์ˆ˜ ํ™œ์šฉ

OpenCV์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‚ด์žฅ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ์—†์ด๋„ ์‰ฝ๊ฒŒ ROI๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

selectROI ํ•จ์ˆ˜ ๋ฌธ๋ฒ•

ret = cv2.selectROI(win_name, img, showCrossHair=True, fromCenter=False)

๋งค๊ฐœ๋ณ€์ˆ˜ ์„ค๋ช…:

cv2.selectROI ๋งค๊ฐœ๋ณ€์ˆ˜

๋งค๊ฐœ๋ณ€์ˆ˜

ํƒ€์ž…

์„ค๋ช…

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 ์‚ฌ์šฉ๋ฒ•:

  1. ๋งˆ์šฐ์Šค๋กœ ์›ํ•˜๋Š” ์˜์—ญ ๋“œ๋ž˜๊ทธ

  2. ์ŠคํŽ˜์ด์Šค๋ฐ” ๋˜๋Š” ์—”ํ„ฐํ‚ค๋กœ ์„ ํƒ ํ™•์ •

  3. 'c' ํ‚ค๋กœ ์„ ํƒ ์ทจ์†Œ

  4. 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: ๋” ๋งŽ์€ ์ •๋ณด ๋ณด์กด, ๋†’์€ ์ •ํ™•๋„

  • ์ ์ ˆํ•œ ๊ท ํ˜•์  ์ฐพ๊ธฐ๊ฐ€ ์ค‘์š”


์ฐธ๊ณ  ์ž๋ฃŒ