goofi v3.0.0

Camera

Frames from a capture device or a video file.

Camera py
out
Type name
graphics:Camera
Plane
graphics
Tags
inputimage
Language
python
Tier
in-process
Bundle
graphics
Source
node-bundles/graphics/camera.py
Availability
available

Slots

SlotDirectionType
outoutputTEXTURE

Parameters

camera

NameTypeDefaultRangeDoc
sourcestringcameracamera | fileA live device, or a file on disk.
deviceint00 … 15Which capture device, counting from the first one.
filestring""The video file to read, when `source` is `file`.
widthint6400 … 4096Width to ask the device for; 0 takes what it offers.
heightint4800 … 4096Height to ask the device for; 0 takes what it offers.
mirrorbooltrueFlip left for right, so the picture moves the way you do.
loopbooltrueStart a file again when it ends.

common

NameTypeDefaultRangeDoc
max_frequencyfloat300.1 … 240Rate cap: the frames a second the node asks for.

Source

The current source of this node, as it stands in the goofi repository at node-bundles/graphics/camera.py.

node-bundles/graphics/camera.py
# goofi: graphics
"""Camera — what a lens or a video file is looking at, as a frame the rest of the patch reads.

The one door in from a camera. Everything that reads a picture takes it from here, so a device is
opened once however many nodes watch what it sees.
"""

import cv2
import numpy as np

import goofi

SOURCES = ["camera", "file"]


class Camera(goofi.Node):
    """Frames from a capture device or a video file."""

    TAGS = ["input", "image"]
    OUTPUTS = {"out": goofi.DataType.TEXTURE}
    PRODUCER = True
    PARAMS = {
        "camera": {
            "source": goofi.StringParam("camera", SOURCES, doc="A live device, or a file on disk."),
            "device": goofi.IntParam(0, 0, 15, doc="Which capture device, counting from the first one."),
            "file": goofi.StringParam("", doc="The video file to read, when `source` is `file`."),
            "width": goofi.IntParam(640, 0, 4096, doc="Width to ask the device for; 0 takes what it offers."),
            "height": goofi.IntParam(480, 0, 4096, doc="Height to ask the device for; 0 takes what it offers."),
            "mirror": goofi.BoolParam(True, doc="Flip left for right, so the picture moves the way you do."),
            "loop": goofi.BoolParam(True, doc="Start a file again when it ends."),
        },
        "common": {
            "max_frequency": goofi.FloatParam(30.0, 0.1, 240.0, doc="Rate cap: the frames a second the node asks for."),
        },
    }

    def setup(self):
        self.cap = None
        self.opened = None

    def process(self):
        p = self.params.camera
        want = (p.source, p.device, p.file, p.width, p.height)
        if want != self.opened:
            self.open(p, want)
        if self.cap is None:
            return None

        ok, frame = self.cap.read()
        if not ok and p.source == "file" and p.loop:
            self.cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
            ok, frame = self.cap.read()
        if not ok:
            return None

        rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        if p.mirror:
            rgb = rgb[:, ::-1]
        return goofi.Texture(np.ascontiguousarray(rgb))

    def open(self, p, want):
        """The device or file the params now name, in place of whatever was open before."""
        self.release()
        self.opened = want
        target = p.device if p.source == "camera" else p.file
        if p.source == "file" and not p.file:
            return
        cap = cv2.VideoCapture(target)
        if not cap.isOpened():
            cap.release()
            raise ValueError(f"no {p.source} at {target!r}")
        if p.width:
            cap.set(cv2.CAP_PROP_FRAME_WIDTH, p.width)
        if p.height:
            cap.set(cv2.CAP_PROP_FRAME_HEIGHT, p.height)
        self.cap = cap

    def release(self):
        if self.cap is not None:
            self.cap.release()
            self.cap = None

    def stop(self):
        self.release()

← All nodes