accessi
Advanced tools
+1
-1
| Metadata-Version: 2.1 | ||
| Name: accessi | ||
| Version: 0.0.4 | ||
| Version: 0.0.5 | ||
| Summary: Library for Siemens Access-i MR Scanner Interface to integrate and control the MR Scanner. | ||
@@ -5,0 +5,0 @@ Author-email: Martin Reinok <m.reinok@student.utwente.nl> |
+2
-2
| [project] | ||
| name = "accessi" | ||
| version = "0.0.4" | ||
| version = "0.0.5" | ||
| authors = [ | ||
@@ -18,2 +18,2 @@ { name="Martin Reinok", email="m.reinok@student.utwente.nl" }, | ||
| Homepage = "https://github.com/martinreinok/accessi-library" | ||
| Issues = "https://github.com/martinreinok/accessi-library/issues" | ||
| Issues = "https://github.com/martinreinok/accessi-library/issues" |
| Metadata-Version: 2.1 | ||
| Name: accessi | ||
| Version: 0.0.4 | ||
| Version: 0.0.5 | ||
| Summary: Library for Siemens Access-i MR Scanner Interface to integrate and control the MR Scanner. | ||
@@ -5,0 +5,0 @@ Author-email: Martin Reinok <m.reinok@student.utwente.nl> |
+188
-20
@@ -15,2 +15,3 @@ """ | ||
| from types import SimpleNamespace | ||
| from math import atan2, degrees | ||
| from typing import Literal | ||
@@ -468,3 +469,4 @@ import numpy as np | ||
| @staticmethod | ||
| def set_slice_orientation_degrees(x_deg, y_deg, z_deg, allow_side_effects=True, index=0): | ||
| def set_slice_orientation_pcs(normal: tuple = (0, 0, 0), phase: tuple = (0, 0, 0), read: tuple = (0, 0, 0), | ||
| allow_side_effects=True, index=0): | ||
| """ | ||
@@ -476,29 +478,118 @@ Response example: | ||
| - "readSet":{"x":-1.0,"y":0,"z":0} | ||
| Don't really understand how the math works | ||
| """ | ||
| # Convert degrees to radians | ||
| x_rad = np.radians(x_deg) | ||
| y_rad = np.radians(y_deg) | ||
| z_rad = np.radians(z_deg) | ||
| url = f"{config.base_url()}/parameter/standard/setSliceOrientationPcs" | ||
| data = {"sessionId": config.session_id, | ||
| "index": index, | ||
| "normal": {"sag": float(normal[0]), "cor": float(normal[1]), "tra": float(normal[2])}, | ||
| "phase": {"sag": float(phase[0]), "cor": float(phase[1]), "tra": float(phase[2])}, | ||
| "read": {"sag": float(read[0]), "cor": float(read[1]), "tra": float(read[2])}, | ||
| "allowSideEffects": allow_side_effects} | ||
| return send_request(url, data, "POST") | ||
| # Calculate the rotation matrices around each axis | ||
| Rx = np.array([[1, 0, 0], [0, np.cos(x_rad), -np.sin(x_rad)], [0, np.sin(x_rad), np.cos(x_rad)]]) | ||
| Ry = np.array([[np.cos(y_rad), 0, np.sin(y_rad)], [0, 1, 0], [-np.sin(y_rad), 0, np.cos(y_rad)]]) | ||
| Rz = np.array([[np.cos(z_rad), -np.sin(z_rad), 0], [np.sin(z_rad), np.cos(z_rad), 0], [0, 0, 1]]) | ||
| @staticmethod | ||
| def get_slice_orientation_pcs(): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "normal":{"x":0,"y":0,"z":-1.0}, | ||
| - "phase":{"x":0,"y":-1.0,"z":0}, | ||
| - "read":{"x":-1.0,"y":0,"z":0} | ||
| """ | ||
| url = f"{config.base_url()}/parameter/standard/getSliceOrientationPcs" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| # Combine the rotation matrices to get the overall rotation matrix | ||
| R = np.dot(Rz, np.dot(Ry, Rx)) | ||
| @staticmethod | ||
| def get_number_of_slice_groups(): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value":2 | ||
| """ | ||
| url = f"{config.base_url()}/parameter/standard/getNumberOfSliceGroups" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| # Extract the orientation vectors (normal, phase, read) from the rotation matrix | ||
| normal = R[:, 2] # Third column | ||
| phase = R[:, 1] # Second column | ||
| read = R[:, 0] # First column | ||
| @staticmethod | ||
| def get_number_of_slices(): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value":2 | ||
| """ | ||
| url = f"{config.base_url()}/parameter/standard/getNumberOfSlices" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| # Normalize the vectors | ||
| normal /= np.linalg.norm(normal) | ||
| phase /= np.linalg.norm(phase) | ||
| read /= np.linalg.norm(read) | ||
| @staticmethod | ||
| def set_slice_orientation_degrees_dcs(roll_degrees, pitch_degrees, yaw_degrees, allow_side_effects=True, index=0): | ||
| """ | ||
| This math is not completely verified but seems to work. | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "normalSet":{"x":0,"y":0,"z":-1.0}, | ||
| - "phaseSet":{"x":0,"y":-1.0,"z":0}, | ||
| - "readSet":{"x":-1.0,"y":0,"z":0} | ||
| """ | ||
| rotation_matrix = ParameterStandard.euler_to_rotation_matrix((roll_degrees, pitch_degrees, yaw_degrees)) | ||
| basis_vectors = np.eye(3) | ||
| rotated_vectors = np.dot(rotation_matrix, basis_vectors.T).T | ||
| normalized_vectors = rotated_vectors / np.linalg.norm(rotated_vectors, axis=1, keepdims=True) | ||
| for vector in normalized_vectors: | ||
| if vector[2] < 0: # Ensure the largest component is positive | ||
| vector *= -1 | ||
| normal = np.array([rotation_matrix[0, 0], rotation_matrix[0, 1], rotation_matrix[0, 2]]) | ||
| phase = np.array([rotation_matrix[1, 0], rotation_matrix[1, 1], rotation_matrix[1, 2]]) | ||
| read = np.array([rotation_matrix[2, 0], rotation_matrix[2, 1], rotation_matrix[2, 2]]) | ||
| # Set the slice orientation using the RAS coordinate system | ||
| return ParameterStandard.set_slice_orientation_dcs(normal, phase, read, allow_side_effects, index) | ||
| @staticmethod | ||
| def euler_to_rotation_matrix(angles=(0, 0, 0)): | ||
| phi, theta, psi = np.radians(angles) | ||
| R_x = np.array([[1, 0, 0], | ||
| [0, np.cos(phi), -np.sin(phi)], | ||
| [0, np.sin(phi), np.cos(phi)]]) | ||
| R_y = np.array([[np.cos(theta), 0, np.sin(theta)], | ||
| [0, 1, 0], | ||
| [-np.sin(theta), 0, np.cos(theta)]]) | ||
| R_z = np.array([[np.cos(psi), -np.sin(psi), 0], | ||
| [np.sin(psi), np.cos(psi), 0], | ||
| [0, 0, 1]]) | ||
| return np.dot(R_z, np.dot(R_y, R_x)) | ||
| @staticmethod | ||
| def get_slice_orientation_degrees_dcs(): | ||
| """ | ||
| This math is not completely verified but seems to work. | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "normal":{180}, | ||
| - "phase":{0}, | ||
| - "read":{180} | ||
| """ | ||
| answer = ParameterStandard.get_slice_orientation_dcs() | ||
| if not answer.result.success: | ||
| return answer.result.reason | ||
| normal = np.array([answer.normal.x, answer.normal.y, answer.normal.z]) | ||
| phase = np.array([answer.phase.x, answer.phase.y, answer.phase.z]) | ||
| read = np.array([answer.read.x, answer.read.y, answer.read.z]) | ||
| rotation_matrix = np.array([normal, phase, read]) | ||
| theta = -np.arcsin(rotation_matrix[0, 2]) # pitch | ||
| psi = np.arctan2(rotation_matrix[0, 1], rotation_matrix[0, 0]) # yaw | ||
| phi = np.arctan2(rotation_matrix[1, 2], rotation_matrix[2, 2]) # roll | ||
| # Convert unit vectors to angles (in degrees) | ||
| answer.normal = np.degrees(phi) | ||
| answer.phase = np.degrees(theta) | ||
| answer.read = np.degrees(psi) | ||
| return answer | ||
| @staticmethod | ||
| def get_slice_thickness(): | ||
@@ -528,2 +619,16 @@ """ | ||
| @staticmethod | ||
| def set_field_of_view_read(value, allow_side_effects=True): | ||
| """ | ||
| Unit:mm | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "valueSet":350.0 | ||
| """ | ||
| url = f"{config.base_url()}/parameter/standard/setFieldOfViewRead" | ||
| data = {"sessionId": config.session_id, | ||
| "value": value, | ||
| "allowSideEffects": allow_side_effects} | ||
| return send_request(url, data, "POST") | ||
| @staticmethod | ||
| def set_slice_thickness(value, allow_side_effects=True): | ||
@@ -565,2 +670,65 @@ """ | ||
| class Table: | ||
| """ | ||
| The table service was newly introduced in version 2 of the Access-i interface, | ||
| to reflect the new / changed table positioning modes that are available for the Numaris X Sola / Vida systems. | ||
| In contrast to Access-i version 1, the table positioning mode is set globally for the whole workflow, | ||
| i.e. not template-/protocol-specific. Therefore a dedicated service was introduced for this purpose. | ||
| """ | ||
| @staticmethod | ||
| def get_current_table_position(): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value":120 | ||
| """ | ||
| url = f"{config.base_url()}/table/getCurrentTablePosition" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| @staticmethod | ||
| def get_table_positioning_mode(): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value":"fix" | ||
| """ | ||
| url = f"{config.base_url()}/table/getTablePositioningMode" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| @staticmethod | ||
| def set_table_positioning_mode(value: Literal["isoCenter", "localRange", "fix"] = "isoCenter"): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value":"fix" | ||
| """ | ||
| url = f"{config.base_url()}/table/setTablePositioningMode" | ||
| data = {"sessionId": config.session_id, "value": value} | ||
| return send_request(url, data, "POST") | ||
| @staticmethod | ||
| def get_fix_table_position(): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"}, | ||
| - "value":123 | ||
| """ | ||
| url = f"{config.base_url()}/table/getFixTablePosition" | ||
| data = {"sessionId": config.session_id} | ||
| return send_request(url, data, "GET") | ||
| @staticmethod | ||
| def set_fix_table_position(value: int): | ||
| """ | ||
| Response example: | ||
| - "result":{"success":true,"reason":"ok","time":"20170608T143325.423"} | ||
| """ | ||
| url = f"{config.base_url()}/table/setFixTablePosition" | ||
| data = {"sessionId": config.session_id, "value": value} | ||
| return send_request(url, data, "POST") | ||
| class Image: | ||
@@ -567,0 +735,0 @@ """ |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
47973
16.63%1009
17.6%