Skip to content

Commit

Permalink
Copy optimisations for JpegEncder
Browse files Browse the repository at this point in the history
Also add a YUV capture test for both JpegEncoder and still JPEG
capture.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
  • Loading branch information
davidplowman committed Feb 13, 2025
1 parent 0a0a5ff commit bfa96c3
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 6 deletions.
19 changes: 14 additions & 5 deletions picamera2/encoders/jpeg_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from picamera2.encoders import Quality
from picamera2.encoders.multi_encoder import MultiEncoder
from picamera2.request import MappedArray


class JpegEncoder(MultiEncoder):
Expand Down Expand Up @@ -42,11 +43,19 @@ def encode_func(self, request, name):
:return: Jpeg image
:rtype: bytes
"""
if self.colour_space is None:
self.colour_space = self.FORMAT_TABLE[request.config[name]["format"]]
array = request.make_array(name)
return simplejpeg.encode_jpeg(array, quality=self.q, colorspace=self.colour_space,
colorsubsampling=self.colour_subsampling)
fmt = request.config[name]["format"]
with MappedArray(request, name) as m:
if fmt == "YUV420":
width, height = request.config[name]['size']
Y = m.array[:height, :width]
reshaped = m.array.reshape((m.array.shape[0] * 2, m.array.strides[0] // 2))
U = reshaped[2 * height: 2 * height + height // 2, :width // 2]
V = reshaped[2 * height + height // 2:, :width // 2]
return simplejpeg.encode_jpeg_yuv_planes(Y, U, V, self.q)
if self.colour_space is None:
self.colour_space = self.FORMAT_TABLE[request.config[name]["format"]]
return simplejpeg.encode_jpeg(m.array, quality=self.q, colorspace=self.colour_space,
colorsubsampling=self.colour_subsampling)

def _setup(self, quality):
# If an explicit quality was specified, use it, otherwise try to preserve any q value
Expand Down
2 changes: 1 addition & 1 deletion picamera2/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def save(self, name: str, file_output: Any, format: Optional[str] = None,
config = self.config.get(name, None)
if config is None:
raise RuntimeError(f'Stream {name!r} is not defined')
if self.FASTER_JPEG and config['format'] != "MJPEG" and \
if (config['format'] == 'YUV420' or (self.FASTER_JPEG and config['format'] != "MJPEG")) and \
self.picam2.helpers._get_format_str(file_output, format) in ('jpg', 'jpeg'):
quality = self.picam2.options.get("quality", 90)
with MappedArray(self, 'main') as m:
Expand Down
1 change: 1 addition & 0 deletions tests/test_list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,4 @@ tests/allocator_test.py
tests/allocator_leak_test.py
tests/wait_cancel_test.py
tests/stride_test.py
tests/yuv_capture.py
21 changes: 21 additions & 0 deletions tests/yuv_capture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/python3
import time

from picamera2 import Picamera2
from picamera2.encoders import JpegEncoder

picam2 = Picamera2()
video_config = picam2.create_video_configuration({"format": "YUV420", "size": (1280, 720)})
picam2.configure(video_config)
encoder = JpegEncoder(q=70)

picam2.start_recording(encoder, 'test.mjpeg')
time.sleep(5)
picam2.stop_recording()

still_config = picam2.create_still_configuration({"format": "YUV420"})
picam2.configure(still_config)
picam2.start()

time.sleep(2)
picam2.capture_file("test.jpg")

0 comments on commit bfa96c3

Please sign in to comment.