Search code examples
pythonopencvcomputer-visionsnapshotroi

Select a static ROI on webcam video on python openCV


I need to selec a ROI (region of interest), or work area, on a live video from my webcam and take a snapshot of ONLY this work area or ROI, but I can't found how to do this.

In this page https://www.learnopencv.com/how-to-select-a-bounding-box-roi-in-opencv-cpp-python/ have a code for draw a ROI but only with images, not a live video.

import cv2
cam = cv2.VideoCapture(0)
cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
cv2.namedWindow("test")

img_counter = 0

while True:
    ret, frame = cam.read()
    cv2.imshow("test", frame)
    if not ret:
        break
    k = cv2.waitKey(1)

    if k % 256 == 27:
        # ESC pressed
        print("Escape hit, closing...")
        break
    elif k % 256 == 32:
        # SPACE pressed
        img_name = "opencv_frame_{}.png".format(img_counter)
        cv2.imwrite(img_name, frame)
        print("{} written!".format(img_name))
        img_counter += 1

cam.release()

cv2.destroyAllWindows()

This code take a snapshot with space key but not draw a ROI area. Thanks in advance!


Solution

  • enter image description here

    Here's a widget to select static ROIs from a video frame. Essentially the idea is to use cv2.setMouseCallback() and event handlers to detect if the mouse has been clicked or released. For this implementation, you can extract coordinates by holding down the left mouse button and dragging to select the desired ROI. You can reset the ROI using the right mouse button. To use the widget, press c to pause the video and start cropping. Then you are free to select the ROI. Once you have selected your ROI, press c again to crop the desired section. To resume the video, press r.

    import cv2
    
    class staticROI(object):
        def __init__(self):
            self.capture = cv2.VideoCapture('fedex.mp4')
    
            # Bounding box reference points and boolean if we are extracting coordinates
            self.image_coordinates = []
            self.extract = False
            self.selected_ROI = False
    
            self.update()
    
        def update(self):
            while True:
                if self.capture.isOpened():
                    # Read frame
                    (self.status, self.frame) = self.capture.read()
                    cv2.imshow('image', self.frame)
                    key = cv2.waitKey(2)
    
                    # Crop image
                    if key == ord('c'):
                        self.clone = self.frame.copy()
                        cv2.namedWindow('image')
                        cv2.setMouseCallback('image', self.extract_coordinates)
                        while True:
                            key = cv2.waitKey(2)
                            cv2.imshow('image', self.clone)
    
                            # Crop and display cropped image
                            if key == ord('c'):
                                self.crop_ROI()
                                self.show_cropped_ROI()
    
                            # Resume video
                            if key == ord('r'):
                                break
                    # Close program with keyboard 'q'
                    if key == ord('q'):
                        cv2.destroyAllWindows()
                        exit(1)
                else:
                    pass
    
        def extract_coordinates(self, event, x, y, flags, parameters):
            # Record starting (x,y) coordinates on left mouse button click
            if event == cv2.EVENT_LBUTTONDOWN:
                self.image_coordinates = [(x,y)]
                self.extract = True
    
            # Record ending (x,y) coordintes on left mouse bottom release
            elif event == cv2.EVENT_LBUTTONUP:
                self.image_coordinates.append((x,y))
                self.extract = False
    
                self.selected_ROI = True
    
                # Draw rectangle around ROI
                cv2.rectangle(self.clone, self.image_coordinates[0], self.image_coordinates[1], (0,255,0), 2)
    
            # Clear drawing boxes on right mouse button click
            elif event == cv2.EVENT_RBUTTONDOWN:
                self.clone = self.frame.copy()
                self.selected_ROI = False
    
        def crop_ROI(self):
            if self.selected_ROI:
                self.cropped_image = self.frame.copy()
    
                x1 = self.image_coordinates[0][0]
                y1 = self.image_coordinates[0][1]
                x2 = self.image_coordinates[1][0]
                y2 = self.image_coordinates[1][1]
    
                self.cropped_image = self.cropped_image[y1:y2, x1:x2]
    
                print('Cropped image: {} {}'.format(self.image_coordinates[0], self.image_coordinates[1]))
            else:
                print('Select ROI to crop before cropping')
    
        def show_cropped_ROI(self):
            cv2.imshow('cropped image', self.cropped_image)
    
    if __name__ == '__main__':
        static_ROI = staticROI()