Skip to content

camera

WebcamStream

Class to handle webcam streaming using a separate thread.

Attributes:

Name Type Description
stream_id int

The ID of the webcam stream.

frame_id int

A counter for the current frame.

vcap VideoCapture

OpenCV video capture object.

width int

The width of the video frame.

height int

The height of the video frame.

fps_input_stream int

Frames per second of the input stream.

grabbed bool

A flag indicating if a frame was successfully grabbed.

frame array

The current frame as a NumPy array.

pil_image Image

The current frame as a PIL image.

stopped bool

A flag indicating if the stream is stopped.

t Thread

The thread used to update the stream.

Source code in inference/core/interfaces/camera/camera.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class WebcamStream:
    """Class to handle webcam streaming using a separate thread.

    Attributes:
        stream_id (int): The ID of the webcam stream.
        frame_id (int): A counter for the current frame.
        vcap (VideoCapture): OpenCV video capture object.
        width (int): The width of the video frame.
        height (int): The height of the video frame.
        fps_input_stream (int): Frames per second of the input stream.
        grabbed (bool): A flag indicating if a frame was successfully grabbed.
        frame (array): The current frame as a NumPy array.
        pil_image (Image): The current frame as a PIL image.
        stopped (bool): A flag indicating if the stream is stopped.
        t (Thread): The thread used to update the stream.
    """

    def __init__(self, stream_id=0, enforce_fps=False):
        """Initialize the webcam stream.

        Args:
            stream_id (int, optional): The ID of the webcam stream. Defaults to 0.
        """
        self.stream_id = stream_id
        self.enforce_fps = enforce_fps
        self.frame_id = 0
        self.vcap = cv2.VideoCapture(self.stream_id)

        for key in os.environ:
            if key.startswith("CV2_CAP_PROP"):
                opencv_prop = key[4:]
                opencv_constant = getattr(cv2, opencv_prop, None)
                if opencv_constant is not None:
                    value = int(os.getenv(key))
                    self.vcap.set(opencv_constant, value)
                    logger.info(f"set {opencv_prop} to {value}")
                else:
                    logger.warn(f"Property {opencv_prop} not found in cv2")

        self.width = int(self.vcap.get(cv2.CAP_PROP_FRAME_WIDTH))
        self.height = int(self.vcap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        self.file_mode = self.vcap.get(cv2.CAP_PROP_FRAME_COUNT) > 0
        if self.enforce_fps and not self.file_mode:
            logger.warn(
                "Ignoring enforce_fps flag for this stream. It is not compatible with streams and will cause the process to crash"
            )
            self.enforce_fps = False
        self.max_fps = None
        if self.vcap.isOpened() is False:
            logger.debug("[Exiting]: Error accessing webcam stream.")
            exit(0)
        self.fps_input_stream = int(self.vcap.get(cv2.CAP_PROP_FPS))
        logger.debug(
            "FPS of webcam hardware/input stream: {}".format(self.fps_input_stream)
        )
        self.grabbed, self.frame = self.vcap.read()
        self.pil_image = Image.fromarray(cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB))
        if self.grabbed is False:
            logger.debug("[Exiting] No more frames to read")
            exit(0)
        self.stopped = True
        self.t = Thread(target=self.update, args=())
        self.t.daemon = True

    def start(self):
        """Start the thread for reading frames."""
        self.stopped = False
        self.t.start()

    def update(self):
        """Update the frame by reading from the webcam."""
        frame_id = 0
        next_frame_time = 0
        t0 = time.perf_counter()
        while True:
            t1 = time.perf_counter()
            if self.stopped is True:
                break

            self.grabbed = self.vcap.grab()
            if self.grabbed is False:
                logger.debug("[Exiting] No more frames to read")
                self.stopped = True
                break
            frame_id += 1
            # We can't retrieve each frame on nano and other lower powered devices quickly enough to keep up with the stream.
            # By default, we will only retrieve frames when we'll be ready process them (determined by self.max_fps).
            if t1 > next_frame_time:
                ret, frame = self.vcap.retrieve()
                if frame is None:
                    logger.debug("[Exiting] Frame not available for read")
                    self.stopped = True
                    break
                logger.debug(
                    f"retrieved frame {frame_id}, effective FPS: {frame_id / (t1 - t0):.2f}"
                )
                self.frame_id = frame_id
                self.frame = frame
                while self.file_mode and self.enforce_fps and self.max_fps is None:
                    # sleep until we have processed the first frame and we know what our FPS should be
                    time.sleep(0.01)
                if self.max_fps is None:
                    self.max_fps = 30
                next_frame_time = t1 + (1 / self.max_fps) + 0.02
            if self.file_mode:
                t2 = time.perf_counter()
                if self.enforce_fps:
                    # when enforce_fps is true, grab video frames 1:1 with inference speed
                    time_to_sleep = next_frame_time - t2
                else:
                    # otherwise, grab at native FPS of the video file
                    time_to_sleep = (1 / self.fps_input_stream) - (t2 - t1)
                if time_to_sleep > 0:
                    time.sleep(time_to_sleep)
        self.vcap.release()

    def read_opencv(self):
        """Read the current frame using OpenCV.

        Returns:
            array, int: The current frame as a NumPy array, and the frame ID.
        """
        return self.frame, self.frame_id

    def stop(self):
        """Stop the webcam stream."""
        self.stopped = True

__init__(stream_id=0, enforce_fps=False)

Initialize the webcam stream.

Parameters:

Name Type Description Default
stream_id int

The ID of the webcam stream. Defaults to 0.

0
Source code in inference/core/interfaces/camera/camera.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def __init__(self, stream_id=0, enforce_fps=False):
    """Initialize the webcam stream.

    Args:
        stream_id (int, optional): The ID of the webcam stream. Defaults to 0.
    """
    self.stream_id = stream_id
    self.enforce_fps = enforce_fps
    self.frame_id = 0
    self.vcap = cv2.VideoCapture(self.stream_id)

    for key in os.environ:
        if key.startswith("CV2_CAP_PROP"):
            opencv_prop = key[4:]
            opencv_constant = getattr(cv2, opencv_prop, None)
            if opencv_constant is not None:
                value = int(os.getenv(key))
                self.vcap.set(opencv_constant, value)
                logger.info(f"set {opencv_prop} to {value}")
            else:
                logger.warn(f"Property {opencv_prop} not found in cv2")

    self.width = int(self.vcap.get(cv2.CAP_PROP_FRAME_WIDTH))
    self.height = int(self.vcap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    self.file_mode = self.vcap.get(cv2.CAP_PROP_FRAME_COUNT) > 0
    if self.enforce_fps and not self.file_mode:
        logger.warn(
            "Ignoring enforce_fps flag for this stream. It is not compatible with streams and will cause the process to crash"
        )
        self.enforce_fps = False
    self.max_fps = None
    if self.vcap.isOpened() is False:
        logger.debug("[Exiting]: Error accessing webcam stream.")
        exit(0)
    self.fps_input_stream = int(self.vcap.get(cv2.CAP_PROP_FPS))
    logger.debug(
        "FPS of webcam hardware/input stream: {}".format(self.fps_input_stream)
    )
    self.grabbed, self.frame = self.vcap.read()
    self.pil_image = Image.fromarray(cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB))
    if self.grabbed is False:
        logger.debug("[Exiting] No more frames to read")
        exit(0)
    self.stopped = True
    self.t = Thread(target=self.update, args=())
    self.t.daemon = True

read_opencv()

Read the current frame using OpenCV.

Returns:

Type Description

array, int: The current frame as a NumPy array, and the frame ID.

Source code in inference/core/interfaces/camera/camera.py
127
128
129
130
131
132
133
def read_opencv(self):
    """Read the current frame using OpenCV.

    Returns:
        array, int: The current frame as a NumPy array, and the frame ID.
    """
    return self.frame, self.frame_id

start()

Start the thread for reading frames.

Source code in inference/core/interfaces/camera/camera.py
75
76
77
78
def start(self):
    """Start the thread for reading frames."""
    self.stopped = False
    self.t.start()

stop()

Stop the webcam stream.

Source code in inference/core/interfaces/camera/camera.py
135
136
137
def stop(self):
    """Stop the webcam stream."""
    self.stopped = True

update()

Update the frame by reading from the webcam.

Source code in inference/core/interfaces/camera/camera.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def update(self):
    """Update the frame by reading from the webcam."""
    frame_id = 0
    next_frame_time = 0
    t0 = time.perf_counter()
    while True:
        t1 = time.perf_counter()
        if self.stopped is True:
            break

        self.grabbed = self.vcap.grab()
        if self.grabbed is False:
            logger.debug("[Exiting] No more frames to read")
            self.stopped = True
            break
        frame_id += 1
        # We can't retrieve each frame on nano and other lower powered devices quickly enough to keep up with the stream.
        # By default, we will only retrieve frames when we'll be ready process them (determined by self.max_fps).
        if t1 > next_frame_time:
            ret, frame = self.vcap.retrieve()
            if frame is None:
                logger.debug("[Exiting] Frame not available for read")
                self.stopped = True
                break
            logger.debug(
                f"retrieved frame {frame_id}, effective FPS: {frame_id / (t1 - t0):.2f}"
            )
            self.frame_id = frame_id
            self.frame = frame
            while self.file_mode and self.enforce_fps and self.max_fps is None:
                # sleep until we have processed the first frame and we know what our FPS should be
                time.sleep(0.01)
            if self.max_fps is None:
                self.max_fps = 30
            next_frame_time = t1 + (1 / self.max_fps) + 0.02
        if self.file_mode:
            t2 = time.perf_counter()
            if self.enforce_fps:
                # when enforce_fps is true, grab video frames 1:1 with inference speed
                time_to_sleep = next_frame_time - t2
            else:
                # otherwise, grab at native FPS of the video file
                time_to_sleep = (1 / self.fps_input_stream) - (t2 - t1)
            if time_to_sleep > 0:
                time.sleep(time_to_sleep)
    self.vcap.release()