Skip to content

Commit 7d9f5cd

Browse files
committed
Copy optimisations for JpegEncder
Also add a YUV capture test for both JpegEncoder and still JPEG capture. Signed-off-by: David Plowman <[email protected]>
1 parent eb487ab commit 7d9f5cd

File tree

4 files changed

+37
-6
lines changed

4 files changed

+37
-6
lines changed

picamera2/encoders/jpeg_encoder.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from picamera2.encoders import Quality
66
from picamera2.encoders.multi_encoder import MultiEncoder
7+
from picamera2.request import MappedArray
78

89

910
class JpegEncoder(MultiEncoder):
@@ -42,11 +43,19 @@ def encode_func(self, request, name):
4243
:return: Jpeg image
4344
:rtype: bytes
4445
"""
45-
if self.colour_space is None:
46-
self.colour_space = self.FORMAT_TABLE[request.config[name]["format"]]
47-
array = request.make_array(name)
48-
return simplejpeg.encode_jpeg(array, quality=self.q, colorspace=self.colour_space,
49-
colorsubsampling=self.colour_subsampling)
46+
fmt = request.config[name]["format"]
47+
with MappedArray(request, name) as m:
48+
if fmt == "YUV420":
49+
width, height = request.config[name]['size']
50+
Y = m.array[:height, :width]
51+
reshaped = m.array.reshape((m.array.shape[0] * 2, m.array.strides[0] // 2))
52+
U = reshaped[2 * height: 2 * height + height // 2, :width // 2]
53+
V = reshaped[2 * height + height // 2:, :width // 2]
54+
return simplejpeg.encode_jpeg_yuv_planes(Y, U, V, self.q)
55+
if self.colour_space is None:
56+
self.colour_space = self.FORMAT_TABLE[request.config[name]["format"]]
57+
return simplejpeg.encode_jpeg(m.array, quality=self.q, colorspace=self.colour_space,
58+
colorsubsampling=self.colour_subsampling)
5059

5160
def _setup(self, quality):
5261
# If an explicit quality was specified, use it, otherwise try to preserve any q value

picamera2/request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def save(self, name: str, file_output: Any, format: Optional[str] = None,
203203
config = self.config.get(name, None)
204204
if config is None:
205205
raise RuntimeError(f'Stream {name!r} is not defined')
206-
if self.FASTER_JPEG and config['format'] != "MJPEG" and \
206+
if (config['format'] == 'YUV420' or (self.FASTER_JPEG and config['format'] != "MJPEG")) and \
207207
self.picam2.helpers._get_format_str(file_output, format) in ('jpg', 'jpeg'):
208208
quality = self.picam2.options.get("quality", 90)
209209
with MappedArray(self, 'main') as m:

tests/test_list.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,4 @@ tests/allocator_test.py
9595
tests/allocator_leak_test.py
9696
tests/wait_cancel_test.py
9797
tests/stride_test.py
98+
tests/yuv_capture.py

tests/yuv_capture.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/python3
2+
import time
3+
4+
from picamera2 import Picamera2
5+
from picamera2.encoders import JpegEncoder
6+
7+
picam2 = Picamera2()
8+
video_config = picam2.create_video_configuration({"format": "YUV420", "size": (1280, 720)})
9+
picam2.configure(video_config)
10+
encoder = JpegEncoder(q=70)
11+
12+
picam2.start_recording(encoder, 'test.mjpeg')
13+
time.sleep(5)
14+
picam2.stop_recording()
15+
16+
still_config = picam2.create_still_configuration({"format": "YUV420"})
17+
picam2.configure(still_config)
18+
picam2.start()
19+
20+
time.sleep(2)
21+
picam2.capture_file("test.jpg")

0 commit comments

Comments
 (0)