Source code for dodal.devices.oav.utils

from enum import IntEnum
from pathlib import Path
from typing import Generator, Tuple

import bluesky.plan_stubs as bps
import numpy as np
from bluesky.utils import Msg
from PIL.Image import Image

from dodal.devices.oav.oav_calculations import camera_coordinates_to_xyz
from dodal.devices.oav.oav_parameters import OAVConfigParams
from dodal.devices.oav.pin_image_recognition import PinTipDetection
from dodal.devices.smargon import Smargon
from dodal.log import LOGGER

Pixel = Tuple[int, int]


class PinNotFoundException(Exception):
    pass


[docs] def bottom_right_from_top_left( top_left: np.ndarray, steps_x: int, steps_y: int, step_size_x: float, step_size_y: float, um_per_pix_x: float, um_per_pix_y: float, ) -> np.ndarray: return np.array( [ # step size is given in mm, pix in um int(steps_x * step_size_x * 1000 / um_per_pix_x + top_left[0]), int(steps_y * step_size_y * 1000 / um_per_pix_y + top_left[1]), ], dtype=np.dtype(int), )
[docs] class ColorMode(IntEnum): """ Enum to store the various color modes of the camera. We use RGB1. """ MONO = 0 BAYER = 1 RGB1 = 2 RGB2 = 3 RGB3 = 4 YUV444 = 5 YUV422 = 6 YUV421 = 7
[docs] class EdgeOutputArrayImageType(IntEnum): """ Enum to store the types of image to tweak the output array. We use Original. """ ORIGINAL = 0 GREYSCALE = 1 PREPROCESSED = 2 CANNY_EDGES = 3 CLOSED_EDGES = 4
[docs] def get_move_required_so_that_beam_is_at_pixel( smargon: Smargon, pixel: Pixel, oav_params: OAVConfigParams ) -> Generator[Msg, None, np.ndarray]: """Calculate the required move so that the given pixel is in the centre of the beam.""" current_motor_xyz = np.array( [ (yield from bps.rd(smargon.x)), (yield from bps.rd(smargon.y)), (yield from bps.rd(smargon.z)), ], dtype=np.float64, ) current_angle = yield from bps.rd(smargon.omega) return calculate_x_y_z_of_pixel(current_motor_xyz, current_angle, pixel, oav_params)
[docs] def calculate_x_y_z_of_pixel( current_x_y_z, current_omega, pixel: Pixel, oav_params: OAVConfigParams ) -> np.ndarray: beam_distance_px: Pixel = oav_params.calculate_beam_distance(*pixel) assert oav_params.micronsPerXPixel assert oav_params.micronsPerYPixel return current_x_y_z + camera_coordinates_to_xyz( beam_distance_px[0], beam_distance_px[1], current_omega, oav_params.micronsPerXPixel, oav_params.micronsPerYPixel, )
[docs] def wait_for_tip_to_be_found( ophyd_pin_tip_detection: PinTipDetection, ) -> Generator[Msg, None, Pixel]: yield from bps.trigger(ophyd_pin_tip_detection, wait=True) found_tip = yield from bps.rd(ophyd_pin_tip_detection.triggered_tip) if found_tip == ophyd_pin_tip_detection.INVALID_POSITION: timeout = yield from bps.rd(ophyd_pin_tip_detection.validity_timeout) raise PinNotFoundException(f"No pin found after {timeout} seconds") return found_tip # type: ignore
[docs] def save_thumbnail(full_file_path: Path, full_image: Image, new_height=192): """Scales an image down to have the height specified in new_height and saves it to the same location as the full image with a t appended to the filename""" thumbnail_path = full_file_path.with_stem(full_file_path.stem + "t") LOGGER.info(f"Saving thumbnail to {thumbnail_path}") full_size = full_image.size new_width = (new_height / full_size[1]) * full_size[0] full_image.thumbnail((new_width, new_height)) full_image.save(thumbnail_path.as_posix())