Skip to content

Commit 4b87b72

Browse files
authored
Merge pull request #107 from LaboratoireMecaniqueLille/feature/send_signal_recording_camera_block
Send signal when recording images with Camera Block
2 parents 16340f9 + d4f68e0 commit 4b87b72

File tree

4 files changed

+108
-11
lines changed

4 files changed

+108
-11
lines changed

docs/source/features.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ Hardware control
256256
Acquires images from a :ref:`Camera` object, and then displays and/or records
257257
the acquired images. It is the base class for other Blocks that can also
258258
perform image processing, in addition to the recording and display. This
259-
Block usually doesn't have input nor output Links.
259+
Block usually doesn't have input nor output Links, but can in some specific
260+
situations.
260261

261262
The examples folder on GitHub contains `several examples of the Camera Block
262263
<https://github.com/LaboratoireMecaniqueLille/crappy/tree/master/examples/
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# coding: utf-8
2+
3+
"""
4+
This example demonstrates the use of the Camera Block, in the case when
5+
recording the acquired images and sending a message to downstream Blocks each
6+
time a new image is saved. It does not require any hardware to run, but
7+
necessitates the opencv-python, scikit-image and Pillow modules to be
8+
installed.
9+
10+
It acquires images on a fake camera, and records part of them at the given
11+
location. Before the test starts, it also lets the user adjust some settings on
12+
the camera in a configuration window. Because images are recorded, no image
13+
processing is performed, and an output Link is defined, a message is sent to
14+
downstream Blocks each time a new image is saved. This message is caught by a
15+
Dashboard Block, that displays the last timestamp when an image was saved. In
16+
addition, a StopButton Block allows stopping the script properly without using
17+
CTRL+C by clicking on a button.
18+
19+
After starting this script, you can play with the parameters in the
20+
configuration window. Once you're done, close the configuration window. Images
21+
will start being recorded at the given location, and the timestamp of the last
22+
saved image should be displayed by the Dashboard. Stop the test after a few
23+
seconds by clicking on the stop button that appears, and check the destination
24+
folder to see the recorded images. You can also hit CTRL+C, but it is not a
25+
clean way to stop Crappy.
26+
"""
27+
28+
import crappy
29+
30+
if __name__ == '__main__':
31+
32+
# The Block in charge of acquiring the images and recording them
33+
# It also displays a configuration windows before the test starts, in which
34+
# the user can tune a few parameters of the Camera
35+
# Here, a fake camera is used so that no hardware is required
36+
# Because save_images is True, no image processing is performed, and an
37+
# output Link is defined, the timestamp and metadata are sent at each new
38+
# saved image over the labels 't(s)' and 'meta'
39+
cam = crappy.blocks.Camera(
40+
'FakeCamera', # Using the FakeCamera camera so that no hardware is
41+
# required
42+
config=True, # Before the test starts, displays a configuration window
43+
# for configuring the camera
44+
display_images=False, # Here, we don't want the images displayed
45+
save_images=True, # The acquired images should be recorded during this
46+
# test
47+
img_extension='tiff', # The images should be saved as .tiff files
48+
save_folder='demo_record_images_send', # The images will be saved in
49+
# this folder, whose path can be relative or absolute
50+
save_period=10, # Only one out of 10 images will be saved, to avoid
51+
# bothering you with tons of images
52+
save_backend=None, # The first available backend will be used
53+
freq=40, # Lowering the default frequency because it's just a demo
54+
55+
# Sticking to default for the other arguments
56+
)
57+
58+
# This Block allows the user to properly exit the script
59+
stop = crappy.blocks.StopButton(
60+
# No specific argument to give for this Block
61+
)
62+
63+
# This Block displays the time value of the moments when an image is saved by
64+
# the Camera Block
65+
# It is here to demonstrate that the information is properly sent to
66+
# downstream Blocks
67+
dash = crappy.blocks.Dashboard(('t(s)',))
68+
69+
# Linking the Blocks together so that the correct information is sent
70+
crappy.link(cam, dash)
71+
72+
# Mandatory line for starting the test, this call is blocking
73+
crappy.start()

src/crappy/blocks/camera.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,19 @@ class Camera(Block):
2424
once.
2525
2626
It takes no input :class:`~crappy.links.Link` in a majority of situations,
27-
and never has output Links. Most of the time, this Block is used for
28-
recording to the desired location the images it acquires. Optionally, the
29-
images can also be displayed in a dedicated window. Both of these features
30-
are however optional, and it is possible to acquire images and not do
31-
anything with them. Several options are available for tuning the record and
32-
the display.
27+
and usually doesn't have output Links neither. The only situations when it
28+
can accept input Links is when an ``image_generator`` is defined, or when
29+
defining a ``software_trig_label``. If ``save_images`` is set to :obj:`True`,
30+
and if an output Link is present, a message is sent to downstream Blocks at
31+
each saved image, containing the timestamp and the metadata of the image.
32+
They are respectively carried by the `'t(s)'` and `'meta'` labels. This is
33+
useful for performing an action conditionally at each new saved image.
34+
35+
Most of the time, this Block is used for recording to the desired location
36+
the images it acquires. Optionally, the images can also be displayed in a
37+
dedicated window. Both of these features are however optional, and it is
38+
possible to acquire images and not do anything with them. Several options are
39+
available for tuning the record and the display.
3340
3441
Before a test starts, this Block can also display a
3542
:class:`~crappy.tool.camera_config.CameraConfig` window in which the user can
@@ -349,13 +356,17 @@ def prepare(self) -> None:
349356
self._disp_lock = RLock()
350357
self._proc_lock = RLock()
351358

352-
# instantiating the ImageSaver CameraProcess
359+
# Instantiating the ImageSaver CameraProcess
353360
if self._save_images:
354361
self.log(logging.INFO, "Instantiating the saver process")
362+
# The ImageSaver sends a message on each saved image only if no
363+
# processing is performed and if there are output Links
364+
send_msg = self.process_proc is None and self.outputs
355365
self._save_proc = ImageSaver(img_extension=self._img_extension,
356366
save_folder=self._save_folder,
357367
save_period=self._save_period,
358-
save_backend=self._save_backend)
368+
save_backend=self._save_backend,
369+
send_msg=send_msg)
359370

360371
# instantiating the Displayer CameraProcess
361372
if self._display_images:
@@ -462,7 +473,7 @@ def get_image(self_) -> (float, np.ndarray):
462473
shape=self._img_shape,
463474
dtype=self._img_dtype,
464475
to_draw_conn=None,
465-
outputs=list(),
476+
outputs=self.outputs,
466477
labels=list(),
467478
log_queue=self._log_queue,
468479
log_level=self._log_level,

src/crappy/blocks/camera_processes/record.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def __init__(self,
4545
img_extension: str = "tiff",
4646
save_folder: Optional[Union[str, Path]] = None,
4747
save_period: int = 1,
48-
save_backend: Optional[str] = None) -> None:
48+
save_backend: Optional[str] = None,
49+
send_msg: bool = False) -> None:
4950
"""Sets the arguments and initializes the parent class.
5051
5152
Args:
@@ -68,6 +69,12 @@ def __init__(self,
6869
Fork), :mod:`cv2` (OpenCV), and :mod:`numpy`. Depending on the machine,
6970
some may be faster or slower. The ``img_extension`` is ignored for the
7071
backend ``'npy'``, that saves the images as raw numpy arrays.
72+
send_msg: In case no processing is performed, and if output Links are
73+
present, this argument is set to :obj:`True`. In that case, a message
74+
containing the timestamp and the metadata of the image is sent to
75+
downstream Blocks each time an image is saved.
76+
77+
.. versionadded:: 2.0.5
7178
"""
7279

7380
super().__init__()
@@ -99,6 +106,7 @@ def __init__(self,
99106
self._save_folder = Path(save_folder)
100107

101108
self._save_period = int(save_period)
109+
self._send_msg: bool = send_msg
102110

103111
self._csv_created = False
104112
self._csv_path = None
@@ -230,3 +238,7 @@ def loop(self) -> None:
230238

231239
elif self._save_backend == 'npy':
232240
np.save(path, self.img)
241+
242+
# Sending the results to the downstream Blocks
243+
if self._send_msg:
244+
self.send({'t(s)': self.metadata['t(s)'], 'meta': self.metadata})

0 commit comments

Comments
 (0)