3

I would appreciate if anyone can help me select an ROI on a video stream while it's playing (I don't want it to pause or capture the first frame). Am I missing something? I've tried setting the frames to the same name.

cv2.selectROI('Frame', frame, False)
cv2.imshow('Frame',frame)
Patrick Yoder
  • 1,065
  • 4
  • 14
  • 19
danny1984
  • 63
  • 1
  • 6

2 Answers2

6

You can't use cv2.selectROI() in that case because the function is blocking, i.e. it stops the execution of your program until you have selected your region of interest (or cancelled it).

To achieve what you want you will need to handle the selection of the ROI yourself. Here is a short example of how you can do that, using two left-clicks to define the ROI and a right-click to erase it.

import cv2, sys

cap = cv2.VideoCapture(sys.argv[1])
cv2.namedWindow('Frame', cv2.WINDOW_NORMAL)

# Our ROI, defined by two points
p1, p2 = None, None
state = 0

# Called every time a mouse event happen
def on_mouse(event, x, y, flags, userdata):
    global state, p1, p2
    
    # Left click
    if event == cv2.EVENT_LBUTTONUP:
        # Select first point
        if state == 0:
            p1 = (x,y)
            state += 1
        # Select second point
        elif state == 1:
            p2 = (x,y)
            state += 1
    # Right click (erase current ROI)
    if event == cv2.EVENT_RBUTTONUP:
        p1, p2 = None, None
        state = 0

# Register the mouse callback
cv2.setMouseCallback('Frame', on_mouse)

while cap.isOpened():
    val, frame = cap.read()
    
    # If a ROI is selected, draw it
    if state > 1:
        cv2.rectangle(frame, p1, p2, (255, 0, 0), 10)
    # Show image
    cv2.imshow('Frame', frame)
    
    # Let OpenCV manage window events
    key = cv2.waitKey(50)
    # If ESCAPE key pressed, stop
    if key == 27:
        cap.release()
vvanpelt
  • 791
  • 1
  • 6
  • 13
0

As @vvanpelt mentioned, cv2.selectROI() causes the video capture being blocked and there is a need to handle the selection of the ROI in other way.

Here I provide a different solution which is more like how cv2.selectROI() works with images, emulating in some way its experience, mainly thanks to cv2.EVENT_LBUTTONDOWN and cv2.EVENT_MOUSEMOVE events on mouse callback. Drag the mouse to draw the ROI.

import cv2

cap = cv2.VideoCapture(0)
cv2.namedWindow('Frame')
if not cap.isOpened():
    exit()

# Our ROI, defined by two points
p1, p2 = (0, 0), (0, 0)
drawing = False  # True while ROI is actively being drawn by mouse
show_drawing = False  # True while ROI is drawn but is pending use or cancel
blue = (255, 0, 0)


def on_mouse(event, x, y, flags, userdata):
    global p1, p2, drawing, show_drawing

    if event == cv2.EVENT_LBUTTONDOWN:
        # Left click down (select first point)
        drawing = True
        show_drawing = True
        p1 = x, y
        p2 = x, y
    elif event == cv2.EVENT_MOUSEMOVE:
        # Drag to second point
        if drawing:
            p2 = x, y
    elif event == cv2.EVENT_LBUTTONUP:
        # Left click up (select second point)
        drawing = False
        p2 = x, y


cv2.setMouseCallback('Frame', on_mouse)

while True:
    val, fr = cap.read()
    if not val:
        break

    if show_drawing:
        # Fix p2 to be always within the frame
        p2 = (
            0 if p2[0] < 0 else (p2[0] if p2[0] < fr.shape[1] else fr.shape[1]),
            0 if p2[1] < 0 else (p2[1] if p2[1] < fr.shape[0] else fr.shape[0])
        )
        cv2.rectangle(fr, p1, p2, blue, 2)
        avg_y = (p1[1] + p2[1]) // 2
        cv2.line(fr, (p1[0], avg_y), (p2[0], avg_y), blue, 2)  # Middle horizontal line
        avg_x = (p1[0] + p2[0]) // 2
        cv2.line(fr, (avg_x, p1[1]), (avg_x, p2[1]), blue, 2)  # Middle vertical line

    cv2.imshow('Frame', fr)

    pressed = cv2.waitKey(1)
    if pressed in [13, 32]:
        # Pressed Enter or Space to use ROI
        drawing = False
        show_drawing = False
        # here do something with ROI points values (p1 and p2)
    elif pressed in [ord('c'), ord('C'), 27]:
        # Pressed C or Esc to cancel ROI
        drawing = False
        show_drawing = False
    elif pressed in [ord('q'), ord('Q')]:
        # Pressed Q to exit
        break

cap.release()
cv2.destroyAllWindows()
Tadeo
  • 3
  • 1
  • 3