Skip to content

Commit

Permalink
docstrings for camera, loop, and syringe
Browse files Browse the repository at this point in the history
  • Loading branch information
bsubbaraman committed Dec 12, 2023
1 parent a255910 commit 16b478c
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 14 deletions.
90 changes: 86 additions & 4 deletions science_jubilee/tools/Camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@


class Camera(Tool):
"""A class representation of a Raspberry Pi camera.
:param Tool: The base tool class
:type Tool: class:`Tool`
"""
def __init__(self, index, name):
"""Constructor method
"""
super().__init__(index, name)
self._camera_matrix = None
self._dist_matrix = None
Expand All @@ -25,7 +32,14 @@ def __init__(self, index, name):
)

def load_coefficients(self, path):
"""Loads camera matrix and distortion coefficients."""
"""Loads camera matrix and distortion coefficients.
:param path: Path to your camera calibration file
:type path: str
:return: A list containing your camera matrix (index 0) and distortion matrix (index 1)
:rtype: list
""" """"""

# N.B. opencv doesn't like opening files in different directories :/
# ToDo: do this from a json, and update calibration process accordingly
# FILE_STORAGE_READ
Expand All @@ -42,7 +56,11 @@ def load_coefficients(self, path):
return [camera_matrix, dist_matrix]

def get_camera_indices(self):
"""Returns valid camera indices for use with OpenCV"""
"""Returns valid camera indices for use with OpenCV
:return: A list of valid camera indices
:rtype: list
"""
index = 0
arr = []
i = 4
Expand All @@ -60,6 +78,15 @@ def get_camera_indices(self):

@requires_active_tool
def get_frame(self, resolution=[1200, 1200], uvc=False):
"""Take a picture and return the image. Compensates for lens distortion using camera calibration file.
:param resolution: Camera resolution, defaults to [1200, 1200]
:type resolution: list, optional
:param uvc: True if the camera is a USB video class (UVC) camera for programmatically setting focus, defaults to False
:type uvc: bool, optional
:return: The captured frame
:rtype: ndarray
"""
with picamera.PiCamera() as camera:
camera.resolution = (1200, 1200)
camera.framerate = 24
Expand All @@ -73,7 +100,18 @@ def get_frame(self, resolution=[1200, 1200], uvc=False):
)
return undistorted

def show_frame(self, frame, grid=False, save=False):
def show_frame(self, frame, grid=False, save=False, save_path="fig.png"):
"""Show a captured frame using matplotlib.
:param frame: The captured frame to show
:type frame: ndarray
:param grid: Show grid lines, defaults to False
:type grid: bool, optional
:param save: Save to file, defaults to False
:type save: bool, optional
:param save_path: File path to save image, defaults to "fig.png"
:type save_path: str, optional
"""
plt.imshow(frame)
plt.title("frame capture")
if grid:
Expand All @@ -83,14 +121,21 @@ def show_frame(self, frame, grid=False, save=False):
[w / 2], [h / 2], marker="o"
) # put a marker in the center of the image
if save:
plt.savefig("fig.png")
plt.savefig(f"{save_path}")
plt.show()

def get_show_frame(self):
"""Get and show a frame.
"""
self.show_frame(self.get_frame())

@requires_active_tool
def video_stream(self, camera_index=0):
"""Start a video stream from the camera.
:param camera_index: The camera index, defaults to 0
:type camera_index: int, optional
"""
cap = cv2.VideoCapture(
camera_index
) # Note that the index corresponding to your camera may not be zero but this is the most common default
Expand Down Expand Up @@ -118,6 +163,15 @@ def video_stream(self, camera_index=0):

@requires_active_tool
def image_wells(self, resolution=[1200, 1200], uvc=False, wells: Well = None):
"""Move to a number of wells to take and show images.
:param resolution: Camera resolution, defaults to [1200, 1200]
:type resolution: list, optional
:param uvc: True if the camera is a USB video class (UVC) camera for programmatically setting focus, defaults to False
:type uvc: bool, optional
:param wells: A list of wells to image, defaults to None
:type wells: :class:`Well`, optional
"""
# TODO: different functions for saving many images, showing images, or getting frames for analysis?
if type(wells) != list:
wells = [wells]
Expand All @@ -133,6 +187,17 @@ def image_wells(self, resolution=[1200, 1200], uvc=False, wells: Well = None):

@requires_active_tool
def get_well_image(self, resolution=[1200, 1200], uvc=False, well: Well = None):
"""Move to a single well to take a picture and return the frame.
:param resolution: Camera resolution, defaults to [1200, 1200]
:type resolution: list, optional
:param uvc: True if the camera is a USB video class (UVC) camera for programmatically setting focus, defaults to False
:type uvc: bool, optional
:param well: The well to image, defaults to None
:type well: :class:`Well`, optional
:return: The captured frame
:rtype: ndarray
"""
x, y, z_bottom = self._get_xyz(well=well)
self._machine.safe_z_movement()
self._machine.move_to(x=x, y=y)
Expand All @@ -143,6 +208,16 @@ def get_well_image(self, resolution=[1200, 1200], uvc=False, well: Well = None):

@staticmethod
def _get_xyz(well: Well = None, location: Tuple[float] = None):
"""Get the (x,y,z) position of a well.
:param well: The well to fetch position of, defaults to None
:type well: :class:`Well`, optional
:param location: Directly specify an (x,y,z) location, defaults to None
:type location: Tuple[float], optional
:raises ValueError: Must specify either a well or a location
:return: The well location
:rtype: Tuple[float, float, float]
"""
if well is not None and location is not None:
raise ValueError("Specify only one of Well or x,y,z location")
elif well is not None:
Expand All @@ -153,6 +228,13 @@ def _get_xyz(well: Well = None, location: Tuple[float] = None):

@staticmethod
def _get_top_bottom(well: Well = None):
"""Get the top and bottom heights of a well.
:param well: The well to fetch position of, defaults to None
:type well: Well, optional
:return: The z-height of the top and bottom of the well
:rtype: Tuple[float, float]
"""
top = well.top
bottom = well.bottom
return top, bottom
47 changes: 46 additions & 1 deletion science_jubilee/tools/Loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,50 @@


class Loop(Tool):
"""A class representation of an inoculation loop.
:param Tool: The base tool class
:type Tool: :class:`Tool`
"""
def __init__(self, index, name):
"""Constructor method
"""
super().__init__(index, name)

@requires_active_tool
def transfer(
self,
s: int = 2000,
source: Well = None,
destination: Well = None,
s: int = 2000,
sweep_x: float = 5,
sweep_y: float = 5,
sweep_z: float = 10,
sweep_speed: float = 100,
up_speed: float = 800,
randomize_pickup: bool = False,
):
"""Dip into a number of (source, destination) well pairs. Used for transferring items from source to destination.
:param source: Source well, defaults to None
:type source: :class:`Well`, optional
:param destination: Destination well, defaults to None
:type destination: :class:`Well`, optional
:param s: Movement speed in mm/min, defaults to 2000
:type s: int, optional
:param sweep_x: Distance (in mm) to sweep in the x direction to pick up material, defaults to 5
:type sweep_x: float, optional
:param sweep_y: Distance (in mm) to sweep in the y direction to pick up material, defaults to 5
:type sweep_y: float, optional
:param sweep_z: Distance (in mm) to move in the z direction to pick up material, defaults to 10
:type sweep_z: float, optional
:param sweep_speed: Speed (in mm/min) at which to sweep to pick up material, defaults to 100
:type sweep_speed: float, optional
:param up_speed: Speed to move out of well (in mm/min), defaults to 800
:type up_speed: float, optional
:param randomize_pickup: Move to a random position in the source well to pick up material, defaults to False
:type randomize_pickup: bool, optional
"""
if type(source) != list:
source = [source]
if type(destination) != list:
Expand Down Expand Up @@ -85,6 +113,16 @@ def transfer(

@staticmethod
def _get_xyz(well: Well = None, location: Tuple[float] = None):
"""Get the (x,y,z) position of a well.
:param well: The well to fetch position of, defaults to None
:type well: :class:`Well`, optional
:param location: Directly specify an (x,y,z) location, defaults to None
:type location: Tuple[float], optional
:raises ValueError: Must specify either a well or a location
:return: The well location
:rtype: Tuple[float, float, float]
"""
if well is not None and location is not None:
raise ValueError("Specify only one of Well or x,y,z location")
elif well is not None:
Expand All @@ -95,6 +133,13 @@ def _get_xyz(well: Well = None, location: Tuple[float] = None):

@staticmethod
def _get_top_bottom(well: Well = None):
"""Get the top and bottom heights of a well.
:param well: The well to fetch position of, defaults to None
:type well: Well, optional
:return: The z-height of the top and bottom of the well
:rtype: Tuple[float, float]
"""
top = well.top
bottom = well.bottom
return top, bottom
Loading

0 comments on commit 16b478c

Please sign in to comment.