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

[SUGGESTION] Add typings and support for local development #1192

Open
KarimAziev opened this issue Jan 20, 2025 · 4 comments
Open

[SUGGESTION] Add typings and support for local development #1192

KarimAziev opened this issue Jan 20, 2025 · 4 comments

Comments

@KarimAziev
Copy link

KarimAziev commented Jan 20, 2025

Is your feature request related to a problem? Please describe.
Picamera2 currently lacks type annotations for many of its methods, making it difficult for developers to understand method signatures, expected inputs, and return types.

Additional context
After buying the Camera Module 3 as a replacement for Camera Module 2, I discovered that Camera Module 3 is not backward compatible with the V4L2/MMAL stack. This was never mentioned in the official documentation.

So now developers have no choice but to rewrite their code to use picamera2. However, the experience of using picamera2 is not the best due to the lack of type annotations and inline documentation, at least for the public methods.

Take, for example, the capture_array method, a highly common method:

def capture_array(self, name="main", wait=None, signal_function=None):
        """Make a 2d image from the next frame in the named stream."""
        return self.dispatch_functions([partial(self.capture_array_, name)], wait, signal_function)

Based on its name (and the minimal docstring), it’s supposed to return the captured frame as a 2D array. Yet, the return type is None in your dispatch code:

def dispatch_functions(self, functions, wait, signal_function=None, immediate=False) -> None:

And this is not an isolated example. Another core method, create_video_configuration, suffers from similar problems:

def create_video_configuration(self,
                               main={},
                               lores=None,
                               raw={},
                               transform=libcamera.Transform(),
                               colour_space=None,
                               buffer_count=6,
                               controls={},
                               display="main",
                               encode="main",
                               queue=True,
                               sensor={},
                               use_case="video") -> dict:

What are the valid keys in the main, lores, or raw dictionaries? What is "lores" even supposed to mean?

What are the possible "format" values (XBGR8888, YUV420, etc.)? Can developers query these programmatically?

Yes, I saw the PDF documentation with descriptions but jumping every time to the PDF documentation is not practical. Documentation should be directly accessible in the code.

I am not exaggerating, because the importance of proper typings grows due to the impossibility of local development without Raspberry Pi hardware, unlike the legacy MMAL/Camera Stack.

There are no mocks, no fallback mechanisms, nothing to let developers stub or test their code.

How can developers expect to build reliable applications when they’re forced to test everything live on Raspberry Pi hardware and without at least proper typings and inline documentation?

Describe the solution you'd like

  • Include proper type annotations to all public methods for arguments and return types.
  • Improving inline documentation.
  • Enabling workflows without requiring live Raspberry hardware.

Picamera2 has incredible potential, but these core issues prevent it from being a polished, developer-ready tool today.

Thank you.

@davidplowman
Copy link
Collaborator

Hi, thanks for the various comments, let me try and work my way through them.

Firstly, I'm sorry it wasn't clear that the v3 camera, GS camera and AI camera, plus any future and third party cameras, are not supported by the legacy stack. I think part of the problem may be that we've been deprecating the old stack for so many years now, that perhaps we have started to forget to keep telling people not to use it! But I can see about trying to add a note about this, perhaps to the camera module discussion.

On the subject of type annotations, yes I'd be happy to try and add more of these. I've been a bit nervous about doing it because I'm not entirely clear how one would check that they're correct (we've had some erroneous type annotations in the past), do you have any suggestions? I'm also happy to try to improve the function documentation, and any more specific suggestions would be appreciated. Perhaps it's a thing that can be done gradually. I'm also happy to accept PRs when people are able to help.

I understand that a number of people don't like pdf documentation. Unfortunately the general style of documentation is set by our documentation team and not by me, but I will pass your comments along.

Finally, I think the chance of having Picamera2 supported on non-Pis is less likely. Picamera2 relies completely upon the libcamera library which has some degree of support for USB webcams, but the features they offer, and the way they have to be driven, the formats they support and so on, are quite different. Realistically, I think the chances of non-Raspberry Pi work being committed to by Raspberry Pi, with all the on-going maintenance and documentation, is low. Sorry about that.

@KarimAziev
Copy link
Author

KarimAziev commented Jan 21, 2025

Hi David, thank you for your thoughtful and honest response!

Regarding the validation of type annotations, may I inquire about the specific issues with annotations that have occurred in the past? Tools like pyright or mypy nowadays do an excellent job. In my opinion, pyright is particularly reliable; I use it every day. Since type annotations are just a convention in Python, they shouldn’t break anything. They might, however, require some code refactoring or type casting. But they can be added gradually over time.

Type annotations don’t just benefit users—they make code maintenance and development faster and more reliable for maintainers as well.

Since I now need to integrate Picamera2 into my project, I suspect I’ll need to dive deep into the Picamera2 codebase. The project allows users to configure on-the-fly cameras, pixel formats, sizes, and switch between GStreamer and v4lctl backends. That being said, I’ll need to thoroughly explore the code to retrieve all configurable parameters. This might give me the opportunity to submit some pull requests with type annotations, where possible, assuming the team is willing to review them.

I can immediately suggest - or even implement - a small but valuable improvement. I noticed that, currently, formats are hardcoded as plain strings throughout the project. For example:

if self.format in ("YUV420", "YVU420"):

The benefit of refactoring the usage into an enum or, at the very least, a Literal string is evident:

# Alternatively:
PixelFormat = Literal["XBGR8888", "XRGB8888", "RGB888", "BGR888", "YUV420"]

For documentation and typing, I think starting with the most commonly used public methods (like capture_array and create_video_configuration) would provide immediate value. I’d be happy to help identify other methods where inline documentation might also be useful.

Including examples within the docstring would further benefit users. I can share specific suggestions if helpful, or even submit PRs targeting small areas for improvement.

Using TypedDict for arguments could further self-document the code without requiring extensive docstrings:

from typing import TypedDict

class CameraConfig(TypedDict):
    pixel_format: PixelFormat
    size: tuple[int, int]
    fps: int

Lastly, regarding non-Raspberry Pi development: I didn’t mean making it work on non-Raspberry Pi systems. Instead, I meant implementing a dummy mock class. For example, consider how GPIO Zero provides an environment variable for mocking GPIO functionality:

import os
os.environ["GPIOZERO_PIN_FACTORY"] = "mock"

This feature was originally intended for GPIO Zero developers who needed to write tests for devices without having the physical hardware connected. However, it’s also proven to be useful for developing GPIO Zero scripts on non-Pi systems.

Currently, even importing Picamera2 on non-Raspberry Pi hardware results in a ModuleNotFoundError: No module named 'libcamera'. A simple mock implementation for Picamera2 could avoid this, allowing developers to build and test applications without needing direct access to live hardware for every iteration.

@davidplowman
Copy link
Collaborator

Hi, and thanks for the reply. There's quite a lot there, so I'll try to cover everything!

There have been times when we've tried to put in some type annotations, although this has lapsed partly because we don't know if they're right. Some problems have been pointed out us, such as here. I will certainly give e.g. pyright a try so maybe this will help. Perhaps it could be included in the routine CI checks that we do in some way?

I'm very happy to accept PRs. Do check the guidelines, though I don't think there's anything prohibitive there. So long as the commits are reasonably organised, posted to the "next" branch, signed, and pass the tests, then we would merge promptly. Folks here at Pi HQ are mostly Linux driver/kernel or C/C++ programmers, so we certainly welcome contributions. One thing to bear in mind is that backward compatibility is very important to us - even where APIs have evolved and wouldn't be what we would implement now, we need to avoid breaking existing user's code.

Finally, from what you say I take it that you'd like some kind of "mock" camera on the Pi itself? So that you can pretend there's a camera there, and run camera code, even when there isn't one? I guess I understand the idea, though I still have some reservations.

Most importantly, lots of the camera problems I see are related to things like: failing to choose the right camera mode, configuring the camera incorrectly, camera controls not having the expected effect, or not at the moment you thought. Not getting the exposure you thought you'd asked for. Images being too dark or bad in some way. None of this would be reproducible with a "dummy" camera (unless you built something very sophisticated), so I'm a bit unconvinced how useful it would really be. If the aim is just to "get some image from a video file and use that" then maybe an application could do that for itself?

Secondly, there are practical considerations. libcamera doesn't have a "dummy" camera implementation, so I guess one would be adding a "libcamera-or-dummy-camera" layer in between Picamera2 and libcamera itself. It sounds like a bit of an upheaval (though I don't mind upheavals when there are compelling arguments in favour).

So I don't know. But I certainly appreciate there's a debate to be had. Perhaps the problem needs more careful scoping out first.

@KarimAziev
Copy link
Author

Thank you for the detailed response! I agree that adding pyright to the CI pipeline shouldn't be too difficult - it only requires Node.js to be installed.

I've gone ahead and created an initial pull request integrating Pyright into the CI pipeline for static type checking.

Regarding the mock camera discussion, I understand your concerns and think it's best to postpone it for now. Possible I should open it as separate issue. I'll focus on type annotations and small improvements in documentation. As I'm currently integrating Picamera2 into my project, it may take a couple of weeks before I submit the next pull request.

Thanks again for your openness and guidance!

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

2 participants