Skip to content

Commit

Permalink
Merge pull request #133 from LaboratoireMecaniqueLille/release/2.0.6
Browse files Browse the repository at this point in the history
Release/2.0.6
  • Loading branch information
WeisLeDocto authored Oct 15, 2024
2 parents e069fd4 + 8c42b28 commit 4b8e08c
Show file tree
Hide file tree
Showing 31 changed files with 255 additions and 281 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Installs the Python dependencies, installs Crappy, and checks that it imports
name: Python Package
name: Test Python Package

on:
# Runs on pull requests targeting the default branch
# Runs on pull requests targeting the default branches
pull_request:
types: [opened, edited, reopened, synchronize]
branches: ["master", "develop"]
Expand All @@ -15,24 +15,35 @@ on:
- cron: '0 12 1 * *'

jobs:
build:
test-python-package:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# Run on all the supported Python versions
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
# Run on all the supported platforms
os: [ubuntu-latest, windows-latest, macos-latest]
# Cannot run for Python 3.7 on macOS as it is no longer supported
exclude:
- os: macos-latest
python-version: 3.7

steps:
# Checkout the repository
- name: Checkout
uses: actions/checkout@v4
# Set up the correct version of Python
- name: Set up Python ${{ matrix.python-version }} on ${{ matrix.os }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Install the build dependencies
- name: Install dependencies
run: python -m pip install --upgrade pip wheel build setuptools
# Install the crappy Python module
- name: Install Crappy
run: python -m pip install .
# Check if the module imports as expected
- name: Import Crappy
run: python -c "import crappy; print(crappy.__version__)"
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from time import gmtime, strftime
from re import match

__version__ = '2.0.5'
__version__ = '2.0.6'

# -- Project information -----------------------------------------------------

Expand Down
100 changes: 100 additions & 0 deletions examples/blocks/generator/generator_custom_condition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# coding: utf-8

"""
This example demonstrates the use of a Generator Block using a user-defined
condition to determine whether to switch to the next Path. It does not require
any specific hardware to run, but necessitates the matplotlib Python module to
be installed.
The Generator Block outputs a signal following a provided path. Several paths
are available, each with a different behavior and different options. They can
be combined to form a custom global path.
Here, the Generator outputs a simple constant signal, that switches to a
different value once the end condition is met. However, unlike the other
Generator example, the stop condition is not one of the standard ones defined
in Crappy, but rather an arbitrary callable defined by the user. Here, the
condition check whether a given file exists, but it could really have been any
other kind of condition.
After starting this script, you should create the file 'test.txt' in the same
folder where this script is located. See how the value of the signal changes
once the file is created. Once you delete the newly created file, the test
should then end, due to the second custom condition. You can also end this demo
earlier by clicking on the stop button that appears. You can also hit CTRL+C,
but it is not a clean way to stop Crappy.
"""

import crappy
from pathlib import Path


def file_exists(data):
"""Returns True if the file 'test.txt' exists at the same level as the
running script, False otherwise.
This arbitrary function can access the data received by the Generator Block,
which is exposed in the data argument as a dictionary.
Args:
data: The data received by the Generator Block since its last loop. The
keys are the labels, and the values a list containing all the received
values for the given label.
"""

return Path('./test.txt').exists()


def file_does_not_exist(data):
"""Returns False if the file 'test.txt' exists at the same level as the
running script, True otherwise.
This arbitrary function can access the data received by the Generator Block,
which is exposed in the data argument as a dictionary.
Args:
data: The data received by the Generator Block since its last loop. The
keys are the labels, and the values a list containing all the received
values for the given label.
"""

return not Path('./test.txt').exists()


if __name__ == '__main__':

# This Generator Block generates a constant signal, and sends it to the
# Dashboard Block for display
# The signal first has a value of 0, then 1.
gen = crappy.blocks.Generator(
path=({'type': 'Constant', 'value': 0,
'condition': file_exists},
{'type': 'Constant', 'value': 1,
'condition': file_does_not_exist}),
# The simple path to generate
# Notice how the functions defined earlier are included in the path and
# associated to the 'condition' key
freq=50, # Lowering the default frequency because it's just a demo
cmd_label='signal', # The label carrying the value of the generated
# signal
path_index_label='index', # This label carries the index of the current
# path
spam=True, # Send a value at each loop, for a nice display on the
# Dashboard

# Sticking to default for the other arguments
)

# This Dashboard displays the signal it receives from the Generator
dash = crappy.blocks.Dashboard(('t(s)', 'signal'))

# This Block allows the user to properly exit the script
stop = crappy.blocks.StopButton(
# No specific argument to give for this Block
)

# Linking the Block so that the information is correctly sent and received
crappy.link(gen, dash)

# Mandatory line for starting the test, this call is blocking
crappy.start()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "crappy"
dynamic = ["readme"]
version = "2.0.5"
version = "2.0.6"
description = "Command and Real-time Acquisition in Parallelized Python"
license = {file = "LICENSE"}
keywords = ["control", "command", "acquisition", "multiprocessing"]
Expand Down
2 changes: 1 addition & 1 deletion src/crappy/__version__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# coding: utf-8

__version__ = '2.0.5'
__version__ = '2.0.6'
15 changes: 8 additions & 7 deletions src/crappy/blocks/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,20 +194,21 @@ def __init__(self,
Ignored if ``save_images`` is :obj:`False`.
.. versionadded:: 1.5.10
save_backend: If ``save_images`` is :obj:`True`, the backend to use for
save_backend: If ``save_images`` is :obj:`True`, the backend to use for
recording the images. It should be one of:
::
'sitk', 'cv2', 'pil', 'npy'
'sitk', 'pil', 'cv2', 'npy'
They correspond to the modules :mod:`SimpleITK`, :mod:`cv2` (OpenCV),
:mod:`PIL` (Pillow Fork), and :mod:`numpy`. Note that the ``'npy'``
They correspond to the modules :mod:`SimpleITK`, :mod:`PIL` (Pillow
Fork), :mod:`cv2` (OpenCV), and :mod:`numpy`. Note that the ``'npy'``
backend saves the images as raw :obj:`numpy.array`, and thus ignores
the ``img_extension`` argument. Depending on the machine, some backends
may be faster or slower. For using each backend, the corresponding
Python must of course be installed. If not provided and ``save_images``
is :obj:`True`, the backends are tried in the same order as given above
and the first available one is used. ``'npy'`` is always available.
Python module must of course be installed. If not provided and
``save_images`` is :obj:`True`, the backends are tried in the same
order as given above and the first available one is used. ``'npy'`` is
always available.
.. versionadded:: 1.5.10
image_generator: A callable taking two :obj:`float` as arguments and
Expand Down
4 changes: 2 additions & 2 deletions src/crappy/blocks/camera_processes/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ def loop(self) -> None:
if self.img.dtype != np.uint8:
self.log(logging.DEBUG, f"Casting displayed image from "
f"{self.img.dtype} to uint8")
if np.max(self.img) > 255:
factor = max(ceil(log2(np.max(self.img) + 1) - 8), 0)
if int(np.max(self.img)) > 255:
factor = max(ceil(log2(int(np.max(self.img)) + 1) - 8), 0)
img = (self.img / 2 ** factor).astype(np.uint8)
else:
img = self.img.astype(np.uint8)
Expand Down
3 changes: 2 additions & 1 deletion src/crappy/blocks/camera_processes/gpu_correl.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ def loop(self) -> None:
self._res_history.append(res)
self._res_history = self._res_history[-self._discard_ref - 1:]

if res > self._discard_limit * np.average(self._res_history[:-1]):
if (res > self._discard_limit *
float(np.average(self._res_history[:-1]))):
self.log(logging.WARNING, "Residual too high, not sending "
"values")
return
Expand Down
21 changes: 15 additions & 6 deletions src/crappy/blocks/camera_processes/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,16 +226,25 @@ def loop(self) -> None:
# Saving the image at the destination path using the chosen backend
self.log(logging.DEBUG, "Saving image")
if self._save_backend == 'sitk':
Sitk.WriteImage(Sitk.GetImageFromArray(self.img), path)
if len(self.img.shape) == 3:
Sitk.WriteImage(Sitk.GetImageFromArray(self.img[:, :, ::-1],
isVector=True), path)
else:
Sitk.WriteImage(Sitk.GetImageFromArray(self.img), path)

elif self._save_backend == 'pil':
if len(self.img.shape) == 3:
PIL.Image.fromarray(self.img[:, :, ::-1]).save(
path, exif={TAGS_INV[key]: val for key, val in self.metadata.items()
if key in TAGS_INV})
else:
PIL.Image.fromarray(self.img).save(
path, exif={TAGS_INV[key]: val for key, val in self.metadata.items()
if key in TAGS_INV})

elif self._save_backend == 'cv2':
cv2.imwrite(path, self.img)

elif self._save_backend == 'pil':
PIL.Image.fromarray(self.img).save(
path, exif={TAGS_INV[key]: val for key, val in self.metadata.items()
if key in TAGS_INV})

elif self._save_backend == 'npy':
np.save(path, self.img)

Expand Down
13 changes: 7 additions & 6 deletions src/crappy/blocks/dic_ve.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,17 @@ def __init__(self,
recording the images. It should be one of:
::
'sitk', 'cv2', 'pil', 'npy'
'sitk', 'pil', 'cv2', 'npy'
They correspond to the modules :mod:`SimpleITK`, :mod:`cv2` (OpenCV),
:mod:`PIL` (Pillow Fork), and :mod:`numpy`. Note that the ``'npy'``
They correspond to the modules :mod:`SimpleITK`, :mod:`PIL` (Pillow
Fork), :mod:`cv2` (OpenCV), and :mod:`numpy`. Note that the ``'npy'``
backend saves the images as raw :obj:`numpy.array`, and thus ignores
the ``img_extension`` argument. Depending on the machine, some backends
may be faster or slower. For using each backend, the corresponding
Python must of course be installed. If not provided and ``save_images``
is :obj:`True`, the backends are tried in the same order as given above
and the first available one is used. ``'npy'`` is always available.
Python module must of course be installed. If not provided and
``save_images`` is :obj:`True`, the backends are tried in the same
order as given above and the first available one is used. ``'npy'`` is
always available.
.. versionadded:: 1.5.10
image_generator: A callable taking two :obj:`float` as arguments and
Expand Down
13 changes: 7 additions & 6 deletions src/crappy/blocks/dis_correl.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,16 +198,17 @@ def __init__(self,
recording the images. It should be one of:
::
'sitk', 'cv2', 'pil', 'npy'
'sitk', 'pil', 'cv2', 'npy'
They correspond to the modules :mod:`SimpleITK`, :mod:`cv2` (OpenCV),
:mod:`PIL` (Pillow Fork), and :mod:`numpy`. Note that the ``'npy'``
They correspond to the modules :mod:`SimpleITK`, :mod:`PIL` (Pillow
Fork), :mod:`cv2` (OpenCV), and :mod:`numpy`. Note that the ``'npy'``
backend saves the images as raw :obj:`numpy.array`, and thus ignores
the ``img_extension`` argument. Depending on the machine, some backends
may be faster or slower. For using each backend, the corresponding
Python must of course be installed. If not provided and ``save_images``
is :obj:`True`, the backends are tried in the same order as given above
and the first available one is used. ``'npy'`` is always available.
Python module must of course be installed. If not provided and
``save_images`` is :obj:`True`, the backends are tried in the same
order as given above and the first available one is used. ``'npy'`` is
always available.
.. versionadded:: 1.5.10
image_generator: A callable taking two :obj:`float` as arguments and
Expand Down
7 changes: 4 additions & 3 deletions src/crappy/blocks/fake_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,11 @@ def loop(self) -> None:

# Calculating the speed based on the command and the mode
if self._mode == 'speed':
speed = np.sign(cmd) * np.min((self._max_speed, np.abs(cmd)))
speed = float(np.sign(cmd)) * float(np.min((self._max_speed,
np.abs(cmd))))
elif self._mode == 'position':
speed = np.sign(cmd - self._current_pos) * np.min(
(self._max_speed, np.abs(cmd - self._current_pos) / delta_t))
speed = float(np.sign(cmd - self._current_pos)) * float(np.min(
(self._max_speed, np.abs(cmd - self._current_pos) / delta_t)))
else:
raise ValueError(f'Invalid mode : {self._mode} !')

Expand Down
13 changes: 7 additions & 6 deletions src/crappy/blocks/gpu_correl.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,16 +200,17 @@ def __init__(self,
recording the images. It should be one of:
::
'sitk', 'cv2', 'pil', 'npy'
'sitk', 'pil', 'cv2', 'npy'
They correspond to the modules :mod:`SimpleITK`, :mod:`cv2` (OpenCV),
:mod:`PIL` (Pillow Fork), and :mod:`numpy`. Note that the ``'npy'``
They correspond to the modules :mod:`SimpleITK`, :mod:`PIL` (Pillow
Fork), :mod:`cv2` (OpenCV), and :mod:`numpy`. Note that the ``'npy'``
backend saves the images as raw :obj:`numpy.array`, and thus ignores
the ``img_extension`` argument. Depending on the machine, some backends
may be faster or slower. For using each backend, the corresponding
Python must of course be installed. If not provided and ``save_images``
is :obj:`True`, the backends are tried in the same order as given above
and the first available one is used. ``'npy'`` is always available.
Python module must of course be installed. If not provided and
``save_images`` is :obj:`True`, the backends are tried in the same
order as given above and the first available one is used. ``'npy'`` is
always available.
.. versionadded:: 1.5.10
image_generator: A callable taking two :obj:`float` as arguments and
Expand Down
13 changes: 7 additions & 6 deletions src/crappy/blocks/gpu_ve.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,17 @@ def __init__(self,
recording the images. It should be one of:
::
'sitk', 'cv2', 'pil', 'npy'
'sitk', 'pil', 'cv2', 'npy'
They correspond to the modules :mod:`SimpleITK`, :mod:`cv2` (OpenCV),
:mod:`PIL` (Pillow Fork), and :mod:`numpy`. Note that the ``'npy'``
They correspond to the modules :mod:`SimpleITK`, :mod:`PIL` (Pillow
Fork), :mod:`cv2` (OpenCV), and :mod:`numpy`. Note that the ``'npy'``
backend saves the images as raw :obj:`numpy.array`, and thus ignores
the ``img_extension`` argument. Depending on the machine, some backends
may be faster or slower. For using each backend, the corresponding
Python must of course be installed. If not provided and ``save_images``
is :obj:`True`, the backends are tried in the same order as given above
and the first available one is used. ``'npy'`` is always available.
Python module must of course be installed. If not provided and
``save_images`` is :obj:`True`, the backends are tried in the same
order as given above and the first available one is used. ``'npy'`` is
always available.
.. versionadded:: 1.5.10
image_generator: A callable taking two :obj:`float` as arguments and
Expand Down
2 changes: 1 addition & 1 deletion src/crappy/blocks/mean.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def loop(self) -> None:
for label, values in data.items():
if self._out_labels is None or label in self._out_labels:
try:
to_send[label] = np.mean(values)
to_send[label] = float(np.mean(values))
except (ValueError, TypeError):
self.log(logging.WARNING, f"Cannot perform averaging on label "
f"{label} with values: {values}")
Expand Down
13 changes: 7 additions & 6 deletions src/crappy/blocks/video_extenso.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,17 @@ def __init__(self,
recording the images. It should be one of:
::
'sitk', 'cv2', 'pil', 'npy'
'sitk', 'pil', 'cv2', 'npy'
They correspond to the modules :mod:`SimpleITK`, :mod:`cv2` (OpenCV),
:mod:`PIL` (Pillow Fork), and :mod:`numpy`. Note that the ``'npy'``
They correspond to the modules :mod:`SimpleITK`, :mod:`PIL` (Pillow
Fork), :mod:`cv2` (OpenCV), and :mod:`numpy`. Note that the ``'npy'``
backend saves the images as raw :obj:`numpy.array`, and thus ignores
the ``img_extension`` argument. Depending on the machine, some backends
may be faster or slower. For using each backend, the corresponding
Python must of course be installed. If not provided and ``save_images``
is :obj:`True`, the backends are tried in the same order as given above
and the first available one is used. ``'npy'`` is always available.
Python module must of course be installed. If not provided and
``save_images`` is :obj:`True`, the backends are tried in the same
order as given above and the first available one is used. ``'npy'`` is
always available.
.. versionadded:: 1.5.10
image_generator: A callable taking two :obj:`float` as arguments and
Expand Down
Loading

0 comments on commit 4b8e08c

Please sign in to comment.