Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for cells sub-module #61

Merged
merged 7 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 110 additions & 1 deletion brainglobe_utils/cells/cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,53 @@

@total_ordering
class Cell:
"""
A class representing a cell with a specific type.

Parameters
----------
pos : str or ElementTree.Element or Dict[str, float] or List of float
Cell position (x, y, z). Input can be a filename containing the x/y/z
position, an xml marker element, a dictionary with keys "x", "y" and
"z", or a list of the positions [x, y, z].

cell_type : int or str or None
Cell type represented by an integer: 1 for unknown/no cell,
2 for cell, -1 for artifact. The usual way to set this is to use:
Cell.ARTIFACT, Cell.CELL, Cell.UNKNOWN, or Cell.NO_CELL as input.
You can also pass "cell" or "no_cell", as well as None.
K-Meech marked this conversation as resolved.
Show resolved Hide resolved

Attributes
----------
x : float
X position.

y : float
Y position.

z : float
Z position.

type : int
Cell type. 1 for unknown/no cell, 2 for cell, -1 for artifact.

transformed_x : float
Transformed x position.

transformed_y : float
Transformed y position.

transformed_z : float
Transformed z position.

structure_id : int
ID of brain structure.

hemisphere : str
Hemisphere of brain.
"""

# integers for self.type
ARTIFACT = -1
CELL = 2
UNKNOWN = 1
Expand Down Expand Up @@ -108,6 +155,32 @@ def transform(
z_offset: float = 0,
integer: bool = False,
) -> None:
"""
Scale and/or offset the cell position. .x / .y / .z will be updated.

Parameters
----------
x_scale : float
Value to scale the x coordinate by.

y_scale : float
Value to scale the y coordinate by.

z_scale : float
Value to scale the z coordinate by.

x_offset : float
Distance to offset in x.

y_offset : float
Distance to offset in y.

z_offset : float
Distance to offset in z.

integer : bool
Whether to round xyz position to nearest integer.
"""
transformed_coords = self._transform(
x_scale, y_scale, z_scale, x_offset, y_offset, z_offset, integer
)
Expand All @@ -123,6 +196,15 @@ def soft_transform(
z_offset: float = 0,
integer: bool = False,
) -> None:
"""
Scale and/or offset the cell position. New values will be saved into
.transformed_x, .transformed_y and .transformed_z. Original .x, .y and
.z will remain unchanged.

See Also
--------
Cell.transform : For description of parameters.
"""
transformed_coords = self._transform(
x_scale, y_scale, z_scale, x_offset, y_offset, z_offset, integer
)
Expand All @@ -133,12 +215,17 @@ def soft_transform(
) = transformed_coords

def flip_x_y(self) -> None:
"""Swap the x and y coordinate"""
self.y, self.x = self.x, self.y

def is_cell(self) -> bool:
return self.type == Cell.CELL

def to_xml_element(self) -> EtElement:
"""
Create an xml element representing the cell, including its xyz
coordinate.
"""
sub_elements = [EtElement("Marker{}".format(axis)) for axis in "XYZ"]
coords = [int(coord) for coord in (self.x, self.y, self.z)]
for sub_element, coord in zip(sub_elements, coords):
Expand All @@ -155,6 +242,7 @@ def to_xml_element(self) -> EtElement:
return element

def __eq__(self, other: Any) -> bool:
"""Return true if position and type of the cells are equal"""
if not isinstance(other, self.__class__):
return False
return (self.x, self.y, self.z, self.type) == (
Expand Down Expand Up @@ -208,6 +296,21 @@ def __hash__(self) -> int:


class UntypedCell(Cell):
"""
A class representing a cell with no type.

Parameters
----------
pos : str or ElementTree.Element or Dict[str, float] or List of float
Cell position (x, y, z). Input can be a filename containing the x/y/z
position, an xml marker element, a dictionary with keys "x", "y" and
"z", or a list of the positions [x, y, z].

See Also
--------
Cell : For description of attributes.
"""

def __init__(
self,
pos: Union[str, ElementTree.Element, Dict[str, float], List[float]],
Expand All @@ -231,17 +334,21 @@ def to_cell(self) -> Cell:


def pos_from_dict(position_dict: Dict[str, float]) -> List[float]:
"""Return [x, y, z] position from dictionary with keys of x, y and z"""
return [position_dict["x"], position_dict["y"], position_dict["z"]]


def pos_from_xml_marker(element: ElementTree.Element) -> List[float]:
"""Return [x, y, z] position from xml marker"""
marker_names = ["Marker{}".format(axis) for axis in "XYZ"]
markers = [element.find(marker_name) for marker_name in marker_names]
pos = [marker.text for marker in markers if marker is not None]
return [float(num) for num in pos if num is not None]


def pos_from_file_name(file_name: str) -> List[float]:
"""Return [x, y, z] position from filename. For example,
'pCellz10y522x392Ch0.tif' would return [392, 522, 10]"""
x = re.findall(r"x\d+", file_name.lower())
y = re.findall(r"y\d+", file_name.lower())
z = re.findall(r"z\d+", file_name.lower())
Expand All @@ -255,7 +362,7 @@ def group_cells_by_z(cells: List[Cell]) -> DefaultDict[float, List[Cell]]:
Parameters
----------
cells : List of Cell
List of cells from cellfinder.cells.cells.Cell
List of cells from brainglobe_utils.cells.cells.Cell

Returns
-------
Expand All @@ -270,4 +377,6 @@ def group_cells_by_z(cells: List[Cell]) -> DefaultDict[float, List[Cell]]:


class MissingCellsError(Exception):
"""Custom exception class for when no cells are found in a file"""

pass
1 change: 1 addition & 0 deletions tests/data/cells/cell_numbers_in_groups_validate.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1,3,7,8,3,1,4,3,1,2,2,1,1,2,5,2,2,2,3,1,1,6,1,1,1,1
1 change: 1 addition & 0 deletions tests/data/cells/z_planes_validate.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1272,1273,1274,1275,1276,1277,1278,1279,1280,1281,1282,1283,1284,1285,1286,1287,1288,1289,1290,1291,1292,1294,1295,1296,1297,1298
Loading
Loading