picam2hdmi 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of picam2hdmi might be problematic. Click here for more details.
- picam2hdmi-0.1.0/LICENSE +21 -0
- picam2hdmi-0.1.0/PKG-INFO +103 -0
- picam2hdmi-0.1.0/README.md +76 -0
- picam2hdmi-0.1.0/picam2hdmi/__init__.py +17 -0
- picam2hdmi-0.1.0/picam2hdmi/capture.py +39 -0
- picam2hdmi-0.1.0/picam2hdmi/cli.py +72 -0
- picam2hdmi-0.1.0/picam2hdmi/output.py +37 -0
- picam2hdmi-0.1.0/picam2hdmi.egg-info/PKG-INFO +103 -0
- picam2hdmi-0.1.0/picam2hdmi.egg-info/SOURCES.txt +13 -0
- picam2hdmi-0.1.0/picam2hdmi.egg-info/dependency_links.txt +1 -0
- picam2hdmi-0.1.0/picam2hdmi.egg-info/entry_points.txt +2 -0
- picam2hdmi-0.1.0/picam2hdmi.egg-info/requires.txt +8 -0
- picam2hdmi-0.1.0/picam2hdmi.egg-info/top_level.txt +1 -0
- picam2hdmi-0.1.0/pyproject.toml +49 -0
- picam2hdmi-0.1.0/setup.cfg +4 -0
picam2hdmi-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Serge Rabyking
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: picam2hdmi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Raspberry Pi camera as a raw-Bayer HDMI source, speaking the bayerlink protocol
|
|
5
|
+
Author: Serge Rabyking
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bayerlink/picam2hdmi
|
|
8
|
+
Project-URL: Source, https://github.com/bayerlink/picam2hdmi
|
|
9
|
+
Project-URL: Issues, https://github.com/bayerlink/picam2hdmi/issues
|
|
10
|
+
Keywords: raspberry-pi,picamera2,bayer,raw,hdmi,fpga,camera,libcamera
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Multimedia :: Video :: Capture
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
16
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy
|
|
21
|
+
Requires-Dist: bayerlink>=0.1.0
|
|
22
|
+
Provides-Extra: pi
|
|
23
|
+
Requires-Dist: picamera2; extra == "pi"
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest; extra == "test"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# picam2hdmi
|
|
29
|
+
|
|
30
|
+
**A Raspberry Pi camera as a raw-Bayer HDMI source.**
|
|
31
|
+
|
|
32
|
+
FPGA boards rarely have a camera connector, but nearly all of them have
|
|
33
|
+
HDMI-in. A Raspberry Pi has the opposite: a first-class camera stack — every
|
|
34
|
+
sensor libcamera supports, drivers, modes, controls — and an HDMI output.
|
|
35
|
+
picam2hdmi turns the Pi into a **sensor module with an HDMI plug**: raw
|
|
36
|
+
12-bit Bayer frames, straight from the sensor's CSI-2 output with the ISP
|
|
37
|
+
bypassed, carried over the display link as bytes and self-described by a
|
|
38
|
+
header line.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
sensor ──CSI-2──▶ Pi (libcamera raw, zero-copy) ──HDMI──▶ any receiver with HDMI-in
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
No transcoding, no compression, no per-sensor code in this tool: the Bayer
|
|
45
|
+
order and bit depth travel in the stream itself, so a receiver built once
|
|
46
|
+
works with every camera the Pi supports.
|
|
47
|
+
|
|
48
|
+
## The protocol: bayerlink
|
|
49
|
+
|
|
50
|
+
The wire format is **[bayerlink](https://github.com/bayerlink/bayerlink)** —
|
|
51
|
+
the display's active area as a byte container, one header line making the
|
|
52
|
+
stream self-describing. The spec, the reference codec and the conformance
|
|
53
|
+
vectors live in the protocol's own repository, because this tool is one
|
|
54
|
+
encoder among several and should not own the contract receivers implement.
|
|
55
|
+
|
|
56
|
+
The codec runs on **both ends**: this tool encodes with it on the Pi, and a
|
|
57
|
+
receiver's host software decodes captured frames with the same package —
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import bayerlink
|
|
61
|
+
|
|
62
|
+
header, raw = bayerlink.decode_frame(captured) # (H, W, 3) uint8 in
|
|
63
|
+
print(header.bayer_order, header.width, header.height, header.frame_seq)
|
|
64
|
+
# raw: (lines, samples) uint16, exactly what the sensor produced
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Status
|
|
68
|
+
|
|
69
|
+
| Piece | State |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| bayerlink v1 protocol, patterns, vectors | **done** — in [bayerlink](https://github.com/bayerlink/bayerlink) |
|
|
72
|
+
| CLI: pattern → container file | **done** |
|
|
73
|
+
| KMS scanout (double-buffered, full-range RGB forced) | next — design in `output.py` |
|
|
74
|
+
| Picamera2 raw capture (zero-copy dmabuf path) | after scanout — design in `capture.py` |
|
|
75
|
+
|
|
76
|
+
## Usage today
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install picam2hdmi # numpy only; add [pi] on the Pi itself
|
|
80
|
+
picam2hdmi pattern --mode counting --width 2028 --height 1078 --out frame.npy
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
That file is bit-for-bit what the HDMI link will carry — receivers can be
|
|
84
|
+
built and tested against it before any cable exists.
|
|
85
|
+
|
|
86
|
+
## The one integration rule
|
|
87
|
+
|
|
88
|
+
The link must deliver bytes unmodified: **full-range RGB, no scaling, no
|
|
89
|
+
overscan, RGB 4:4:4**. The most common failure is a limited-range clamp
|
|
90
|
+
(16–235) quietly destroying sample values; the `checker` and `corners`
|
|
91
|
+
patterns exist to catch exactly that on day one. Details and the receiver
|
|
92
|
+
rate rule are in [PROTOCOL.md](PROTOCOL.md).
|
|
93
|
+
|
|
94
|
+
## Why this exists
|
|
95
|
+
|
|
96
|
+
Built as the sensor front-end for FPGA image pipelines — for example, as the
|
|
97
|
+
input to hardware generated with [np2hw](https://github.com/lanserge/np2hw)
|
|
98
|
+
— but useful to anyone who wants real sensor data into a board without
|
|
99
|
+
MIPI hardware, deserialisers, or per-sensor bring-up.
|
|
100
|
+
|
|
101
|
+
## Licence
|
|
102
|
+
|
|
103
|
+
MIT.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# picam2hdmi
|
|
2
|
+
|
|
3
|
+
**A Raspberry Pi camera as a raw-Bayer HDMI source.**
|
|
4
|
+
|
|
5
|
+
FPGA boards rarely have a camera connector, but nearly all of them have
|
|
6
|
+
HDMI-in. A Raspberry Pi has the opposite: a first-class camera stack — every
|
|
7
|
+
sensor libcamera supports, drivers, modes, controls — and an HDMI output.
|
|
8
|
+
picam2hdmi turns the Pi into a **sensor module with an HDMI plug**: raw
|
|
9
|
+
12-bit Bayer frames, straight from the sensor's CSI-2 output with the ISP
|
|
10
|
+
bypassed, carried over the display link as bytes and self-described by a
|
|
11
|
+
header line.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
sensor ──CSI-2──▶ Pi (libcamera raw, zero-copy) ──HDMI──▶ any receiver with HDMI-in
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
No transcoding, no compression, no per-sensor code in this tool: the Bayer
|
|
18
|
+
order and bit depth travel in the stream itself, so a receiver built once
|
|
19
|
+
works with every camera the Pi supports.
|
|
20
|
+
|
|
21
|
+
## The protocol: bayerlink
|
|
22
|
+
|
|
23
|
+
The wire format is **[bayerlink](https://github.com/bayerlink/bayerlink)** —
|
|
24
|
+
the display's active area as a byte container, one header line making the
|
|
25
|
+
stream self-describing. The spec, the reference codec and the conformance
|
|
26
|
+
vectors live in the protocol's own repository, because this tool is one
|
|
27
|
+
encoder among several and should not own the contract receivers implement.
|
|
28
|
+
|
|
29
|
+
The codec runs on **both ends**: this tool encodes with it on the Pi, and a
|
|
30
|
+
receiver's host software decodes captured frames with the same package —
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import bayerlink
|
|
34
|
+
|
|
35
|
+
header, raw = bayerlink.decode_frame(captured) # (H, W, 3) uint8 in
|
|
36
|
+
print(header.bayer_order, header.width, header.height, header.frame_seq)
|
|
37
|
+
# raw: (lines, samples) uint16, exactly what the sensor produced
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Status
|
|
41
|
+
|
|
42
|
+
| Piece | State |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| bayerlink v1 protocol, patterns, vectors | **done** — in [bayerlink](https://github.com/bayerlink/bayerlink) |
|
|
45
|
+
| CLI: pattern → container file | **done** |
|
|
46
|
+
| KMS scanout (double-buffered, full-range RGB forced) | next — design in `output.py` |
|
|
47
|
+
| Picamera2 raw capture (zero-copy dmabuf path) | after scanout — design in `capture.py` |
|
|
48
|
+
|
|
49
|
+
## Usage today
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install picam2hdmi # numpy only; add [pi] on the Pi itself
|
|
53
|
+
picam2hdmi pattern --mode counting --width 2028 --height 1078 --out frame.npy
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
That file is bit-for-bit what the HDMI link will carry — receivers can be
|
|
57
|
+
built and tested against it before any cable exists.
|
|
58
|
+
|
|
59
|
+
## The one integration rule
|
|
60
|
+
|
|
61
|
+
The link must deliver bytes unmodified: **full-range RGB, no scaling, no
|
|
62
|
+
overscan, RGB 4:4:4**. The most common failure is a limited-range clamp
|
|
63
|
+
(16–235) quietly destroying sample values; the `checker` and `corners`
|
|
64
|
+
patterns exist to catch exactly that on day one. Details and the receiver
|
|
65
|
+
rate rule are in [PROTOCOL.md](PROTOCOL.md).
|
|
66
|
+
|
|
67
|
+
## Why this exists
|
|
68
|
+
|
|
69
|
+
Built as the sensor front-end for FPGA image pipelines — for example, as the
|
|
70
|
+
input to hardware generated with [np2hw](https://github.com/lanserge/np2hw)
|
|
71
|
+
— but useful to anyone who wants real sensor data into a board without
|
|
72
|
+
MIPI hardware, deserialisers, or per-sensor bring-up.
|
|
73
|
+
|
|
74
|
+
## Licence
|
|
75
|
+
|
|
76
|
+
MIT.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""picam2hdmi -- a Raspberry Pi camera as a bayerlink HDMI source.
|
|
2
|
+
|
|
3
|
+
The wire format is the bayerlink protocol (github.com/bayerlink/bayerlink);
|
|
4
|
+
this tool is its reference ENCODER for the Pi. The protocol package runs on
|
|
5
|
+
both ends of the link, so the container-building lives there and what lives
|
|
6
|
+
here is only what is the Pi's: capture (Picamera2) and scanout (KMS).
|
|
7
|
+
"""
|
|
8
|
+
from bayerlink import ( # noqa: F401 (re-exported for convenience)
|
|
9
|
+
Header,
|
|
10
|
+
decode_frame,
|
|
11
|
+
encode_frame,
|
|
12
|
+
pattern,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__version__ = "0.1.0"
|
|
16
|
+
|
|
17
|
+
__all__ = ["Header", "encode_frame", "decode_frame", "pattern"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Capture: raw Bayer frames from the camera, via the Linux camera stack.
|
|
2
|
+
|
|
3
|
+
NOT IMPLEMENTED YET -- this module states the intended design so the work is
|
|
4
|
+
specified before it is written. It only runs on a Pi with a camera attached.
|
|
5
|
+
|
|
6
|
+
Intended design
|
|
7
|
+
---------------
|
|
8
|
+
|
|
9
|
+
Picamera2 (libcamera), raw stream only:
|
|
10
|
+
|
|
11
|
+
- configure the RAW stream at the chosen sensor mode (for the reference
|
|
12
|
+
setup: the HQ camera's 2028x1080 12-bit mode -- 2028 obeys the 1080p rate
|
|
13
|
+
rule in PROTOCOL.md);
|
|
14
|
+
- the ISP is NOT in the loop: the raw stream is the sensor's own packed
|
|
15
|
+
output, DMA-written by the CSI receiver. The Bayer order and bit depth
|
|
16
|
+
come from libcamera's reported format and go straight into the bayerlink
|
|
17
|
+
fourcc -- this tool never holds a sensor register table, which is the
|
|
18
|
+
point: the kernel drivers that know those tables run in THEIR project,
|
|
19
|
+
under their licence, and every camera libcamera supports works here
|
|
20
|
+
without this tool learning anything about it;
|
|
21
|
+
- hand each completed buffer to output.stream(), preferably as a dmabuf so
|
|
22
|
+
the scanout flips the capture buffer itself (zero copies), otherwise via
|
|
23
|
+
one packed-line copy into the container;
|
|
24
|
+
- stamp frame_seq once per delivered camera frame, so receivers can
|
|
25
|
+
deduplicate scanout repeats.
|
|
26
|
+
|
|
27
|
+
Auto-exposure and auto-white-balance are DISABLED by default: a raw source
|
|
28
|
+
should be deterministic unless the operator asks otherwise. Fixed exposure
|
|
29
|
+
and gain are CLI arguments.
|
|
30
|
+
"""
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def frames(mode=None, exposure_us=None, analogue_gain=None): # pragma: no cover
|
|
35
|
+
"""Yield (raw_frame, bayer_order, frame_seq) from the camera. Pi-only."""
|
|
36
|
+
raise NotImplementedError(
|
|
37
|
+
"Picamera2 capture is the milestone after scanout; the module "
|
|
38
|
+
"docstring is its design. Until then, pattern.generate() supplies "
|
|
39
|
+
"frames with the same shape and dtype.")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Command line: `picam2hdmi pattern` works anywhere; `stream` needs a Pi."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from bayerlink import pattern, protocol
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main(argv=None) -> int:
|
|
13
|
+
parser = argparse.ArgumentParser(
|
|
14
|
+
prog="picam2hdmi",
|
|
15
|
+
description="Raw Bayer over HDMI, speaking bayerlink v1 "
|
|
16
|
+
"(github.com/bayerlink/bayerlink).")
|
|
17
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
18
|
+
|
|
19
|
+
pat = sub.add_parser(
|
|
20
|
+
"pattern",
|
|
21
|
+
help="encode a test pattern into a bayerlink container (runs anywhere)")
|
|
22
|
+
pat.add_argument("--mode", default="counting",
|
|
23
|
+
choices=sorted(pattern.PATTERNS))
|
|
24
|
+
pat.add_argument("--width", type=int, default=2028,
|
|
25
|
+
help="samples per line (default: HQ camera 2028)")
|
|
26
|
+
pat.add_argument("--height", type=int, default=1078,
|
|
27
|
+
help="camera lines; a 1080-line display carries at most "
|
|
28
|
+
"1079 (one line is the header), 1078 keeps whole "
|
|
29
|
+
"CFA rows")
|
|
30
|
+
pat.add_argument("--display", default="1920x1080",
|
|
31
|
+
help="container geometry, WxH (default 1920x1080)")
|
|
32
|
+
pat.add_argument("--bayer", default="RGGB",
|
|
33
|
+
choices=sorted(protocol.BAYER_PHASE))
|
|
34
|
+
pat.add_argument("--frame-seq", type=int, default=0)
|
|
35
|
+
pat.add_argument("--out", required=True,
|
|
36
|
+
help=".npy for the (H,W,3) container, .bin for raw bytes")
|
|
37
|
+
|
|
38
|
+
stream = sub.add_parser(
|
|
39
|
+
"stream", help="scan out over HDMI (Raspberry Pi only; next milestone)")
|
|
40
|
+
stream.add_argument("--source", default="pattern",
|
|
41
|
+
choices=["pattern", "camera"])
|
|
42
|
+
|
|
43
|
+
args = parser.parse_args(argv)
|
|
44
|
+
|
|
45
|
+
if args.command == "pattern":
|
|
46
|
+
width, _, height = args.display.partition("x")
|
|
47
|
+
display = (int(width), int(height))
|
|
48
|
+
raw = pattern.generate(args.mode, args.width, args.height)
|
|
49
|
+
frame = protocol.encode_frame(
|
|
50
|
+
raw, args.bayer, frame_seq=args.frame_seq, display=display,
|
|
51
|
+
flags=protocol.FLAG_TEST_PATTERN)
|
|
52
|
+
if args.out.endswith(".npy"):
|
|
53
|
+
np.save(args.out, frame)
|
|
54
|
+
else:
|
|
55
|
+
frame.tofile(args.out)
|
|
56
|
+
# Prove the file by decoding it back before claiming success.
|
|
57
|
+
header, decoded = protocol.decode_frame(frame)
|
|
58
|
+
assert np.array_equal(decoded, raw)
|
|
59
|
+
print(f"wrote {args.out}: {args.mode} {args.width}x{args.height} "
|
|
60
|
+
f"({header.fourcc}, seq {header.frame_seq}) in "
|
|
61
|
+
f"{display[0]}x{display[1]}, round-trip verified")
|
|
62
|
+
return 0
|
|
63
|
+
|
|
64
|
+
if args.command == "stream":
|
|
65
|
+
from . import capture, output # noqa: F401 (their docstrings are the design)
|
|
66
|
+
output.stream(frames=None)
|
|
67
|
+
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
sys.exit(main())
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Scanout: putting encoded frames on the HDMI connector.
|
|
2
|
+
|
|
3
|
+
NOT IMPLEMENTED YET -- this module states the intended design so the work is
|
|
4
|
+
specified before it is written. The protocol and patterns are complete and
|
|
5
|
+
tested; this is the next milestone, and it only runs on a Pi.
|
|
6
|
+
|
|
7
|
+
Intended design
|
|
8
|
+
---------------
|
|
9
|
+
|
|
10
|
+
DRM/KMS directly (no X, no Wayland, no desktop):
|
|
11
|
+
|
|
12
|
+
- open the DRM device, pick the connector's mode (1080p30 for the reference
|
|
13
|
+
setup -- see PROTOCOL.md's rate rule for why not 720p);
|
|
14
|
+
- create TWO dumb buffers in the container format and page-flip between
|
|
15
|
+
them on vsync: the classic double buffer. A flip replaces the WHOLE frame
|
|
16
|
+
atomically, so a receiver never sees half an update -- the same reasoning
|
|
17
|
+
as shadow registers, applied to pixels;
|
|
18
|
+
- force FULL-RANGE RGB on the connector ("Broadcast RGB" property where the
|
|
19
|
+
driver exposes it, plus the firmware setting), because a limited-range
|
|
20
|
+
link clamps exactly the values raw data lives on;
|
|
21
|
+
- with a camera attached, import the capture buffer as a dmabuf and flip it
|
|
22
|
+
directly where stride and format allow -- zero copies from sensor DMA to
|
|
23
|
+
scanout. Where strides disagree, fall back to one memcpy per line.
|
|
24
|
+
|
|
25
|
+
The container format is chosen so the MEMORY byte order matches PROTOCOL.md;
|
|
26
|
+
whether that is DRM_FORMAT_RGB888 or BGR888 on a given platform is resolved
|
|
27
|
+
once, in the phase-0 pattern loopback, and recorded in the board notes.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def stream(frames, mode=(1920, 1080, 30)): # pragma: no cover - hardware only
|
|
33
|
+
"""Scan an iterable of encoded frames out over HDMI. Pi-only; see above."""
|
|
34
|
+
raise NotImplementedError(
|
|
35
|
+
"KMS scanout is the next milestone; the module docstring is its "
|
|
36
|
+
"design. Until then, `picam2hdmi pattern --out frame.npy` produces "
|
|
37
|
+
"the exact bytes this will display.")
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: picam2hdmi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Raspberry Pi camera as a raw-Bayer HDMI source, speaking the bayerlink protocol
|
|
5
|
+
Author: Serge Rabyking
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bayerlink/picam2hdmi
|
|
8
|
+
Project-URL: Source, https://github.com/bayerlink/picam2hdmi
|
|
9
|
+
Project-URL: Issues, https://github.com/bayerlink/picam2hdmi/issues
|
|
10
|
+
Keywords: raspberry-pi,picamera2,bayer,raw,hdmi,fpga,camera,libcamera
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Multimedia :: Video :: Capture
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
16
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy
|
|
21
|
+
Requires-Dist: bayerlink>=0.1.0
|
|
22
|
+
Provides-Extra: pi
|
|
23
|
+
Requires-Dist: picamera2; extra == "pi"
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest; extra == "test"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# picam2hdmi
|
|
29
|
+
|
|
30
|
+
**A Raspberry Pi camera as a raw-Bayer HDMI source.**
|
|
31
|
+
|
|
32
|
+
FPGA boards rarely have a camera connector, but nearly all of them have
|
|
33
|
+
HDMI-in. A Raspberry Pi has the opposite: a first-class camera stack — every
|
|
34
|
+
sensor libcamera supports, drivers, modes, controls — and an HDMI output.
|
|
35
|
+
picam2hdmi turns the Pi into a **sensor module with an HDMI plug**: raw
|
|
36
|
+
12-bit Bayer frames, straight from the sensor's CSI-2 output with the ISP
|
|
37
|
+
bypassed, carried over the display link as bytes and self-described by a
|
|
38
|
+
header line.
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
sensor ──CSI-2──▶ Pi (libcamera raw, zero-copy) ──HDMI──▶ any receiver with HDMI-in
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
No transcoding, no compression, no per-sensor code in this tool: the Bayer
|
|
45
|
+
order and bit depth travel in the stream itself, so a receiver built once
|
|
46
|
+
works with every camera the Pi supports.
|
|
47
|
+
|
|
48
|
+
## The protocol: bayerlink
|
|
49
|
+
|
|
50
|
+
The wire format is **[bayerlink](https://github.com/bayerlink/bayerlink)** —
|
|
51
|
+
the display's active area as a byte container, one header line making the
|
|
52
|
+
stream self-describing. The spec, the reference codec and the conformance
|
|
53
|
+
vectors live in the protocol's own repository, because this tool is one
|
|
54
|
+
encoder among several and should not own the contract receivers implement.
|
|
55
|
+
|
|
56
|
+
The codec runs on **both ends**: this tool encodes with it on the Pi, and a
|
|
57
|
+
receiver's host software decodes captured frames with the same package —
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import bayerlink
|
|
61
|
+
|
|
62
|
+
header, raw = bayerlink.decode_frame(captured) # (H, W, 3) uint8 in
|
|
63
|
+
print(header.bayer_order, header.width, header.height, header.frame_seq)
|
|
64
|
+
# raw: (lines, samples) uint16, exactly what the sensor produced
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Status
|
|
68
|
+
|
|
69
|
+
| Piece | State |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| bayerlink v1 protocol, patterns, vectors | **done** — in [bayerlink](https://github.com/bayerlink/bayerlink) |
|
|
72
|
+
| CLI: pattern → container file | **done** |
|
|
73
|
+
| KMS scanout (double-buffered, full-range RGB forced) | next — design in `output.py` |
|
|
74
|
+
| Picamera2 raw capture (zero-copy dmabuf path) | after scanout — design in `capture.py` |
|
|
75
|
+
|
|
76
|
+
## Usage today
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install picam2hdmi # numpy only; add [pi] on the Pi itself
|
|
80
|
+
picam2hdmi pattern --mode counting --width 2028 --height 1078 --out frame.npy
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
That file is bit-for-bit what the HDMI link will carry — receivers can be
|
|
84
|
+
built and tested against it before any cable exists.
|
|
85
|
+
|
|
86
|
+
## The one integration rule
|
|
87
|
+
|
|
88
|
+
The link must deliver bytes unmodified: **full-range RGB, no scaling, no
|
|
89
|
+
overscan, RGB 4:4:4**. The most common failure is a limited-range clamp
|
|
90
|
+
(16–235) quietly destroying sample values; the `checker` and `corners`
|
|
91
|
+
patterns exist to catch exactly that on day one. Details and the receiver
|
|
92
|
+
rate rule are in [PROTOCOL.md](PROTOCOL.md).
|
|
93
|
+
|
|
94
|
+
## Why this exists
|
|
95
|
+
|
|
96
|
+
Built as the sensor front-end for FPGA image pipelines — for example, as the
|
|
97
|
+
input to hardware generated with [np2hw](https://github.com/lanserge/np2hw)
|
|
98
|
+
— but useful to anyone who wants real sensor data into a board without
|
|
99
|
+
MIPI hardware, deserialisers, or per-sensor bring-up.
|
|
100
|
+
|
|
101
|
+
## Licence
|
|
102
|
+
|
|
103
|
+
MIT.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
picam2hdmi/__init__.py
|
|
5
|
+
picam2hdmi/capture.py
|
|
6
|
+
picam2hdmi/cli.py
|
|
7
|
+
picam2hdmi/output.py
|
|
8
|
+
picam2hdmi.egg-info/PKG-INFO
|
|
9
|
+
picam2hdmi.egg-info/SOURCES.txt
|
|
10
|
+
picam2hdmi.egg-info/dependency_links.txt
|
|
11
|
+
picam2hdmi.egg-info/entry_points.txt
|
|
12
|
+
picam2hdmi.egg-info/requires.txt
|
|
13
|
+
picam2hdmi.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
picam2hdmi
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "picam2hdmi"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Raspberry Pi camera as a raw-Bayer HDMI source, speaking the bayerlink protocol"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "Serge Rabyking" }]
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
keywords = ["raspberry-pi", "picamera2", "bayer", "raw", "hdmi", "fpga", "camera", "libcamera"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Multimedia :: Video :: Capture",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Image Processing",
|
|
21
|
+
"Topic :: Software Development :: Embedded Systems",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"numpy",
|
|
25
|
+
# The wire format. Lives in the protocol's own repository so every
|
|
26
|
+
# encoder and receiver conforms to one spec; this tool re-exports its
|
|
27
|
+
# codec and adds only what is the Pi's.
|
|
28
|
+
"bayerlink>=0.1.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
# The capture backend, on the Pi itself. Everything else -- the protocol, the
|
|
33
|
+
# patterns, decoding captured frames on the receiver's host -- needs numpy only.
|
|
34
|
+
pi = ["picamera2"]
|
|
35
|
+
test = ["pytest"]
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/bayerlink/picam2hdmi"
|
|
39
|
+
Source = "https://github.com/bayerlink/picam2hdmi"
|
|
40
|
+
Issues = "https://github.com/bayerlink/picam2hdmi/issues"
|
|
41
|
+
|
|
42
|
+
[project.scripts]
|
|
43
|
+
picam2hdmi = "picam2hdmi.cli:main"
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.packages.find]
|
|
46
|
+
include = ["picam2hdmi*"]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
testpaths = ["tests"]
|