Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hardcoding timestamps onto image/video frames with the picamera library #687

Open
mfreeborn opened this issue May 2, 2021 · 0 comments
Open

Comments

@mfreeborn
Copy link

Asking this here as I've received no responses on the Raspberry Pi forums or the Raspberry Pi Stack Exchange

My goal is for videos and images to have timestamps on them for an application I'm writing which uses the Python picamera library.

My current implementation modifies the annotate_text attribute using an image and video custom encoder. The reason I've opted for a custom encoder approach rather than a custom output is that I need to hook into the capturing apparatus and set the timestamp immediately before the capture is taken. I also want the timestamps to be there by default; from an ease of use point of view, I shouldn't need to think about the timestamps everywhere else in the code, whether I'm using capture(), capture_continuous() or start_recording(), or using other custom outputs.

Here's my implementation for videos, which I think is as good as I can get:

class TimestampedVideoEncoder(picamerax.PiCookedVideoEncoder):
    """Extended video encoder which inserts timestamps on video frames."""

    def stamp(self):
        self.parent.annotate_text = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")

    def start(self, output, motion_output=None):
        # this method is called once just before capturing commences, so here we can set
        # the initial timestamp
        self.stamp()
        super().start(output, motion_output)

    def _callback_write(self, buf):
        # this method is called at least once per frame, so here we can update the timestamp.
        # We will only do so after whole I frames and P frames, just to reduce the number of
        # calls to .stamp() a little.
        # Note that this actually sets the timestamp that will appear on the subsequent frame, which
        # doesn't matter for videos unless we are are recording at a very low framerate. I think this
        # is a limitation that we have to live with - I don't see how we can hook into a point immediately
        # before a new frame starts being captured
        ret = super()._callback_write(buf)
        if (buf.flags & mmal.MMAL_BUFFER_HEADER_FLAG_FRAME_END) and not (
            buf.flags & mmal.MMAL_BUFFER_HEADER_FLAG_CONFIG
        ):
            self.stamp()
        return ret

What I'm struggling a bit more with is images. I'm testing with capture_continuous and I'm getting some behaviour that I can't explain: despite confirming that annotate_text is set to the correct timestamp before each call to super().start(output), the timestamp that visually appears on each image is only updated every other capture. So, when I set the capture_continuous to take a picture every 5 seconds, the timestamps will be:

  • [image 1 -> 09:00:00],
  • [image 2 -> 09:00:00],
  • [image 3 -> 09:00:10],
  • [image 4 -> 09:00:10],
  • [image 5 -> 09:00:20],
  • etc...

Here's the code for the image encoder:

class TimestampedImageEncoder(picamerax.PiCookedOneImageEncoder):
    """Extended image encoder which inserts timestamps on image frames."""

    def stamp(self):
        self.parent.annotate_text = datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")

    def start(self, output):
        # this method is called once just before capturing commences, so here we can set
        # the timestamp before the capture process takes place
        self.stamp()
        super().start(output)

Is there an elegant way to achieve what I'm trying to do?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant