diff --git a/docs/recipes1.rst b/docs/recipes1.rst index 5a237ebf..212a16fc 100644 --- a/docs/recipes1.rst +++ b/docs/recipes1.rst @@ -866,7 +866,7 @@ interfaces. Overlaying text on the output ============================= -The camera includes a rudimentary annotation facility which permits up to 31 +The camera includes a rudimentary annotation facility which permits up to 255 characters of ASCII text to be overlayed on all output (including the preview, image captures and video recordings). To achieve this, simply assign a string to the :attr:`~picamera.PiCamera.annotate_text` attribute:: @@ -900,8 +900,9 @@ With a little ingenuity, it's possible to display longer strings:: camera.annotate_text = camera.annotate_text[1:31] + c time.sleep(0.1) -And of course, it can be used to display (and embed) a timestamp in -recordings:: +And of course, it can be used to display (and embed) a timestamp in recordings +(this recipe also demonstrates drawing a background behind the timestamp for +contrast with the :attr:`~picamera.PiCamera.annotate_bg` attribute):: import picamera import datetime as dt @@ -910,6 +911,7 @@ recordings:: camera.resolution = (1280, 720) camera.framerate = 24 camera.start_preview() + camera.annotate_bg = True camera.annotate_text = dt.datetime.now().strftime('%Y-%m-%d %H:%M:%S') camera.start_recording('timestamped.h264') start = dt.datetime.now() diff --git a/picamera/camera.py b/picamera/camera.py index c53269ad..fc589035 100644 --- a/picamera/camera.py +++ b/picamera/camera.py @@ -2908,7 +2908,8 @@ def _set_image_effect_params(self, value): mmal.mmal_port_parameter_set(self._camera[0].control, mp.hdr), prefix="Failed to set image effect parameters") self._image_effect_params = value - image_effect_params = property(_get_image_effect_params, _set_image_effect_params, doc=""" + image_effect_params = property( + _get_image_effect_params, _set_image_effect_params, doc=""" Retrieves or sets the parameters for the current :attr:`effect `. @@ -3372,26 +3373,29 @@ def _set_preview_window(self, value): def _get_annotate_text(self): self._check_camera_open() - mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_T( + mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_ANNOTATE, - ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_T) + ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T) )) mmal_check( mmal.mmal_port_parameter_get(self._camera[0].control, mp.hdr), - prefix="Failed to get annotation status") + prefix="Failed to get annotation text") if mp.enable: return mp.text.decode('ascii') else: return '' def _set_annotate_text(self, value): self._check_camera_open() - mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_T( + mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_ANNOTATE, - ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_T) + ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T) )) - if value: + mmal_check( + mmal.mmal_port_parameter_get(self._camera[0].control, mp.hdr), + prefix="Failed to get annotation status") + if value or bool(mp.show_frame_num): mp.enable = True try: mp.text = value.encode('ascii') @@ -3401,17 +3405,88 @@ def _set_annotate_text(self, value): mp.enable = False mmal_check( mmal.mmal_port_parameter_set(self._camera[0].control, mp.hdr), - prefix="Failed to set annotation status") + prefix="Failed to set annotation text") annotate_text = property(_get_annotate_text, _set_annotate_text, doc=""" Retrieves or sets a text annotation for all output. - When queried, :attr:`annotate_text` property returns returns the - current annotation (if no annotation has been set, this is simply - a blank string). + When queried, the :attr:`annotate_text` property returns the current + annotation (if no annotation has been set, this is simply a blank + string). When set, the property immediately applies the annotation to the preview (if it is running) and to any future captures or video - recording. Strings longer than 31 characters, or strings containing + recording. Strings longer than 255 characters, or strings containing non-ASCII characters will raise a :exc:`PiCameraValueError`. The default value is ``''``. """) + + def _get_annotate_frame_num(self): + self._check_camera_open() + mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T( + mmal.MMAL_PARAMETER_HEADER_T( + mmal.MMAL_PARAMETER_ANNOTATE, + ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T) + )) + mmal_check( + mmal.mmal_port_parameter_get(self._camera[0].control, mp.hdr), + prefix="Failed to get annotation frame number") + return mp.show_frame_num != mmal.MMAL_FALSE + def _set_annotate_frame_num(self, value): + self._check_camera_open() + mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T( + mmal.MMAL_PARAMETER_HEADER_T( + mmal.MMAL_PARAMETER_ANNOTATE, + ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T) + )) + mmal_check( + mmal.mmal_port_parameter_get(self._camera[0].control, mp.hdr), + prefix="Failed to get annotation status") + mp.enable = bool(value) or bool(mp.text) + mp.show_frame_num = bool(value) + mmal_check( + mmal.mmal_port_parameter_set(self._camera[0].control, mp.hdr), + prefix="Failed to set annotation frame number") + annotate_frame_num = property( + _get_annotate_frame_num, _set_annotate_frame_num, doc=""" + Controls whether the current frame number is drawn as an annotation. + + The :attr:`annotate_frame_num` attribute is a bool indicating whether + or not the current frame number is rendered as an annotation, similar + to :attr:`annotate_text`. The default is ``False``. + """) + + def _get_annotate_background(self): + self._check_camera_open() + mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T( + mmal.MMAL_PARAMETER_HEADER_T( + mmal.MMAL_PARAMETER_ANNOTATE, + ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T) + )) + mmal_check( + mmal.mmal_port_parameter_get(self._camera[0].control, mp.hdr), + prefix="Failed to get annotation background") + return mp.black_text_background != mmal.MMAL_FALSE + def _set_annotate_background(self, value): + self._check_camera_open() + mp = mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T( + mmal.MMAL_PARAMETER_HEADER_T( + mmal.MMAL_PARAMETER_ANNOTATE, + ct.sizeof(mmal.MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T) + )) + mmal_check( + mmal.mmal_port_parameter_get(self._camera[0].control, mp.hdr), + prefix="Failed to get annotation status") + mp.black_text_background = bool(value) + mmal_check( + mmal.mmal_port_parameter_set(self._camera[0].control, mp.hdr), + prefix="Failed to set annotation background") + annotate_background = property( + _get_annotate_background, _set_annotate_background, doc=""" + Controls whether a black background is drawn behind the annotation. + + The :attr:`annotate_bg` attribute is a bool indicating whether or not a + black background will be drawn behind the :attr:`annotation text + `. The background will appear in all output including + image captures and video recording. The default is ``False``. + """) + diff --git a/picamera/mmal.py b/picamera/mmal.py index 26af38ba..a76f9fde 100644 --- a/picamera/mmal.py +++ b/picamera/mmal.py @@ -1134,6 +1134,22 @@ class MMAL_PARAMETER_CAMERA_ANNOTATE_T(ct.Structure): ('show_motion', MMAL_BOOL_T), ] +MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2 = 256 + +class MMAL_PARAMETER_CAMERA_ANNOTATE_V2_T(ct.Structure): + _fields_ = [ + ('hdr', MMAL_PARAMETER_HEADER_T), + ('enable', MMAL_BOOL_T), + ('show_shutter', MMAL_BOOL_T), + ('show_analog_gain', MMAL_BOOL_T), + ('show_lens', MMAL_BOOL_T), + ('show_caf', MMAL_BOOL_T), + ('show_motion', MMAL_BOOL_T), + ('show_frame_num', MMAL_BOOL_T), + ('black_text_background', MMAL_BOOL_T), + ('text', ct.c_char * MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2), + ] + MMAL_STEREOSCOPIC_MODE_T = ct.c_uint32 # enum ( MMAL_STEREOSCOPIC_MODE_NONE, diff --git a/picamera/renderers.py b/picamera/renderers.py index 01c4d44b..39f2d7bc 100644 --- a/picamera/renderers.py +++ b/picamera/renderers.py @@ -219,7 +219,7 @@ def _set_fullscreen(self, value): ct.sizeof(mmal.MMAL_DISPLAYREGION_T) ), set=mmal.MMAL_DISPLAY_SET_FULLSCREEN, - fullscreen=(mmal.MMAL_FALSE, mmal.MMAL_TRUE)[bool(value)] + fullscreen=bool(value) ) mmal_check( mmal.mmal_port_parameter_set(self.renderer[0].input[0], mp.hdr), diff --git a/tests/test_attr.py b/tests/test_attr.py index 4f3427f7..625b1295 100644 --- a/tests/test_attr.py +++ b/tests/test_attr.py @@ -90,10 +90,16 @@ def test_annotate_text(camera, previewing): camera.annotate_text = 'foo bar baz quux xyzzy' assert camera.annotate_text == u'foo bar baz quux xyzzy' with pytest.raises(picamera.PiCameraValueError): - camera.annotate_text = 'this value is way too long for this attribute' + camera.annotate_text = 'abcd' * 64 with pytest.raises(picamera.PiCameraValueError): camera.annotate_text = 'Oh lá lá' +def test_annotate_background(camera, previewing): + boolean_attr(camera, 'annotate_background') + +def test_annotate_frame_num(camera, previewing): + boolean_attr(camera, 'annotate_frame_num') + def test_awb_mode(camera, previewing): keyword_attr(camera, 'awb_mode', camera.AWB_MODES)