Skip to content

Commit 16468c9

Browse files
author
Tom Willemsen
authored
Merge pull request #162 from DiamondLightSource/reshape_in_ophyd
Reshape OAV stream in ophyd and switch to using ophyd-async
2 parents 65229df + 2cf74ef commit 16468c9

File tree

7 files changed

+16
-112
lines changed

7 files changed

+16
-112
lines changed

pyproject.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,18 @@ classifiers = [
1414
]
1515
description = "Ophyd devices and other utils that could be used across DLS beamlines"
1616
dependencies = [
17-
"ophyd@git+https://github.com/DominicOram/ophyd@31a1762435bcb275d2555068233549885eab02e7", # Switch back to just "ophyd" once ophyd#1148, ophyd#1155, ophyd#1140 merged.
17+
"ophyd",
18+
"ophyd-async",
1819
"bluesky",
1920
"pyepics",
2021
"dataclasses-json",
2122
"pillow",
2223
"requests",
2324
"graypy",
2425
"pydantic<2.0",
25-
"opencv-python-headless", # For pin-tip detection.
26-
"aioca", # Required for CA support with Ophyd V2.
27-
"p4p", # Required for PVA support with Ophyd V2.
26+
"opencv-python-headless", # For pin-tip detection.
27+
"aioca", # Required for CA support with ophyd-async.
28+
"p4p", # Required for PVA support with ophyd-async.
2829
]
2930
dynamic = ["version"]
3031
license.file = "LICENSE"

src/dodal/beamlines/beamline_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from bluesky.run_engine import call_in_bluesky_event_loop
55
from ophyd import Device as OphydV1Device
66
from ophyd.sim import make_fake_device
7-
from ophyd.v2.core import Device as OphydV2Device
8-
from ophyd.v2.core import wait_for_connection as v2_device_wait_for_connection
7+
from ophyd_async.core import Device as OphydV2Device
8+
from ophyd_async.core import wait_for_connection as v2_device_wait_for_connection
99

1010
from dodal.utils import AnyDevice, BeamlinePrefix, skip_device
1111

src/dodal/devices/oav/pin_image_recognition/__init__.py

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
from typing import Optional, Tuple, Type, TypeVar
55

66
import numpy as np
7-
from bluesky.protocols import Descriptor
7+
from bluesky.protocols import Descriptor, Readable, Reading
88
from numpy.typing import NDArray
9-
from ophyd.v2.core import Device, Readable, Reading, SimSignalBackend
10-
from ophyd.v2.epics import SignalR, SignalRW, epics_signal_r
9+
from ophyd_async.core import Device, SignalR, SignalRW, SimSignalBackend
10+
from ophyd_async.epics.signal import epics_signal_r
1111

1212
from dodal.devices.oav.pin_image_recognition.utils import (
1313
ARRAY_PROCESSING_FUNCTIONS_MAP,
@@ -48,13 +48,6 @@ def __init__(self, prefix: str, name: str = ""):
4848
NDArray[np.uint8], f"pva://{prefix}PVA:ARRAY"
4949
)
5050

51-
self.oav_width: SignalR[int] = epics_signal_r(
52-
int, f"{prefix}PVA:ArraySize1_RBV"
53-
)
54-
self.oav_height: SignalR[int] = epics_signal_r(
55-
int, f"{prefix}PVA:ArraySize2_RBV"
56-
)
57-
5851
# Soft parameters for pin-tip detection.
5952
self.timeout: SignalRW[float] = _create_soft_signal(float, "timeout")
6053
self.preprocess: SignalRW[int] = _create_soft_signal(int, "preprocess")
@@ -84,7 +77,6 @@ async def _get_tip_position(
8477
Returns tuple of:
8578
((tip_x, tip_y), timestamp)
8679
"""
87-
8880
preprocess_key = await self.preprocess.get_value()
8981
preprocess_iter = await self.preprocess_iterations.get_value()
9082
preprocess_ksize = await self.preprocess_ksize.get_value()
@@ -111,30 +103,9 @@ async def _get_tip_position(
111103
array_data: NDArray[np.uint8] = array_reading[""]["value"]
112104
timestamp: float = array_reading[""]["timestamp"]
113105

114-
height: int = await self.oav_height.get_value()
115-
width: int = await self.oav_width.get_value()
116-
117-
num_pixels: int = height * width # type: ignore
118-
value_len = array_data.shape[0]
119-
120106
try:
121-
if value_len == num_pixels * 3:
122-
# RGB data
123-
value = array_data.reshape(height, width, 3)
124-
elif value_len == num_pixels:
125-
# Grayscale data
126-
value = array_data.reshape(height, width)
127-
else:
128-
# Something else?
129-
raise ValueError(
130-
"Unexpected data array size: expected {} (grayscale data) or {} (rgb data), got {}",
131-
num_pixels,
132-
num_pixels * 3,
133-
value_len,
134-
)
135-
136107
start_time = time.time()
137-
location = sample_detection.processArray(value)
108+
location = sample_detection.processArray(array_data)
138109
end_time = time.time()
139110
LOGGER.debug(
140111
"Sample location detection took {}ms".format(

src/dodal/devices/oav/pin_image_recognition/manual_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ async def acquire():
2727
try:
2828
import matplotlib.pyplot as plt
2929

30-
plt.imshow(img[""]["value"].reshape(768, 1024, 3))
30+
plt.imshow(img[""]["value"])
3131
plt.show()
3232
except ImportError:
3333
print("matplotlib not available; cannot show acquired image")

src/dodal/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
WritesExternalAssets,
3838
)
3939
from ophyd.device import Device as OphydV1Device
40-
from ophyd.v2.core import Device as OphydV2Device
40+
from ophyd_async.core import Device as OphydV2Device
4141

4242
try:
4343
from typing import TypeAlias

tests/beamlines/unit_tests/test_beamline_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from ophyd import Device
66
from ophyd.device import Device as OphydV1Device
77
from ophyd.sim import FakeEpicsSignal
8-
from ophyd.v2.core import Device as OphydV2Device
8+
from ophyd_async.core import Device as OphydV2Device
99

1010
from dodal.beamlines import beamline_utils, i03
1111
from dodal.devices.aperturescatterguard import ApertureScatterguard

tests/devices/unit_tests/oav/image_recognition/test_pin_tip_detect.py

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import asyncio
2-
from unittest.mock import MagicMock, patch
2+
from unittest.mock import patch
33

4-
import numpy as np
54
import pytest
6-
from numpy.typing import NDArray
7-
from ophyd.v2.core import set_sim_value
5+
from ophyd_async.core import set_sim_value
86

97
from dodal.devices.oav.pin_image_recognition import MxSampleDetect, PinTipDetection
10-
from dodal.devices.oav.pin_image_recognition.utils import SampleLocation
118

129
EVENT_LOOP = asyncio.new_event_loop()
1310

@@ -90,68 +87,3 @@ async def test_invalid_processing_func_uses_identity_function():
9087
# Assert captured preprocess function is the identitiy function
9188
arg = object()
9289
assert arg == captured_func(arg)
93-
94-
95-
@pytest.mark.parametrize(
96-
"input_array,height,width,reshaped",
97-
[
98-
(np.zeros(shape=(1,)), 1, 1, np.zeros(shape=(1, 1))),
99-
(np.zeros(shape=(3,)), 1, 1, np.zeros(shape=(1, 1, 3))),
100-
(np.zeros(shape=(1920 * 1080)), 1080, 1920, np.zeros(shape=(1080, 1920))),
101-
(
102-
np.zeros(shape=(1920 * 1080 * 3)),
103-
1080,
104-
1920,
105-
np.zeros(shape=(1080, 1920, 3)),
106-
),
107-
],
108-
)
109-
def test_when_data_supplied_THEN_reshaped_correctly_before_call_to_process_array(
110-
input_array: NDArray, height: int, width: int, reshaped: NDArray
111-
):
112-
device = EVENT_LOOP.run_until_complete(_get_pin_tip_detection_device())
113-
114-
device.array_data._backend._set_value(input_array) # type: ignore
115-
set_sim_value(device.oav_height, height)
116-
set_sim_value(device.oav_width, width)
117-
118-
MxSampleDetect.processArray = MagicMock(
119-
autospec=True,
120-
return_value=SampleLocation(
121-
tip_x=10, tip_y=20, edge_bottom=np.array([]), edge_top=np.array([])
122-
),
123-
)
124-
125-
result = EVENT_LOOP.run_until_complete(device.read())
126-
127-
MxSampleDetect.processArray.assert_called_once()
128-
np.testing.assert_array_equal(MxSampleDetect.processArray.call_args[0][0], reshaped)
129-
130-
assert result[""]["value"] == (10, 20)
131-
132-
133-
@pytest.mark.parametrize(
134-
"input_array,height,width",
135-
[
136-
(np.zeros(shape=(0,)), 1080, 1920),
137-
(np.zeros(shape=(1920 * 1080 * 2)), 1080, 1920),
138-
],
139-
)
140-
def test_when_invalid_data_length_supplied_THEN_no_call_to_process_array(
141-
input_array: NDArray, height: int, width: int
142-
):
143-
device = EVENT_LOOP.run_until_complete(_get_pin_tip_detection_device())
144-
145-
set_sim_value(device.array_data, input_array)
146-
set_sim_value(device.oav_height, height)
147-
set_sim_value(device.oav_width, width)
148-
149-
MxSampleDetect.processArray = MagicMock(
150-
autospec=True,
151-
)
152-
153-
result = EVENT_LOOP.run_until_complete(device.read())
154-
155-
MxSampleDetect.processArray.assert_not_called()
156-
157-
assert result[""]["value"] == (None, None)

0 commit comments

Comments
 (0)