Skip to content
Snippets Groups Projects
Commit 6e5b633e authored by Simon Wittl's avatar Simon Wittl
Browse files

added volume io

parent 54d8355e
No related branches found
No related tags found
No related merge requests found
Showing
with 246 additions and 38 deletions
from rq_controller.common import PyVolume
from rq_controller.common.io.rq_json import RqJsonWriter, RqJsonLoader
from pathlib import Path
FOLDER = Path('./example/data')
def main():
volume = PyVolume.dummy()
print(f'Shape (x / y / z): {volume.shape}')
msg = volume.as_message()
print(f'Number of slices: {len(msg.slices)}')
volume_2 = PyVolume.from_message(msg)
print(f'Shape (x / y / z): {volume_2.shape}')
writer = RqJsonWriter()
writer.write_volume(writer.get_volume_save_path_i(FOLDER, 1), volume_2)
loader = RqJsonLoader()
volume_3 = loader.load_volume(writer.get_volume_save_path_i(FOLDER, 1))
print(f'Shape (x / y / z): {volume_3.shape}')
if __name__ == '__main__':
main()
Metadata-Version: 2.1 Metadata-Version: 2.1
Name: rq_controller Name: rq-controller
Version: 0.0.0 Version: 0.0.0
Summary: TODO: Package description Summary: TODO: Package description
Maintainer: root Maintainer: root
Maintainer-email: simon.wittl@th-deg.de Maintainer-email: simon.wittl@th-deg.de
License: TODO: License declaration License: TODO: License declaration
Requires-Dist: setuptools Platform: UNKNOWN
UNKNOWN
.gitignore
README.md README.md
package.xml package.xml
setup.cfg setup.cfg
setup.py setup.py
.vscode/c_cpp_properties.json
.vscode/settings.json
example/projection_example.py
example/data/projection_00001.geom-json
example/data/projection_00001.tif
resource/rq_controller resource/rq_controller
rq_controller/__init__.py rq_controller/__init__.py
rq_controller/rq_workflow_client.py rq_controller/rq_workflow.py
rq_controller.egg-info/PKG-INFO rq_controller.egg-info/PKG-INFO
rq_controller.egg-info/SOURCES.txt rq_controller.egg-info/SOURCES.txt
rq_controller.egg-info/dependency_links.txt rq_controller.egg-info/dependency_links.txt
...@@ -15,12 +21,23 @@ rq_controller/common/__init__.py ...@@ -15,12 +21,23 @@ rq_controller/common/__init__.py
rq_controller/common/projection.py rq_controller/common/projection.py
rq_controller/common/projection_geometry.py rq_controller/common/projection_geometry.py
rq_controller/common/region_of_intrest.py rq_controller/common/region_of_intrest.py
rq_controller/common/volume.py
rq_controller/common/__pycache__/__init__.cpython-38.pyc
rq_controller/common/__pycache__/projection.cpython-38.pyc
rq_controller/common/__pycache__/projection_geometry.cpython-38.pyc
rq_controller/common/__pycache__/region_of_intrest.cpython-38.pyc
rq_controller/common/io/__init__.py rq_controller/common/io/__init__.py
rq_controller/common/io/loader.py rq_controller/common/io/loader.py
rq_controller/common/io/writer.py rq_controller/common/io/writer.py
rq_controller/common/io/__pycache__/__init__.cpython-38.pyc
rq_controller/common/io/__pycache__/loader.cpython-38.pyc
rq_controller/common/io/__pycache__/writer.cpython-38.pyc
rq_controller/common/io/rq_json/__init__.py rq_controller/common/io/rq_json/__init__.py
rq_controller/common/io/rq_json/load.py rq_controller/common/io/rq_json/json_load.py
rq_controller/common/io/rq_json/write.py rq_controller/common/io/rq_json/json_write.py
rq_controller/common/io/rq_json/__pycache__/__init__.cpython-38.pyc
rq_controller/common/io/rq_json/__pycache__/load.cpython-38.pyc
rq_controller/common/io/rq_json/__pycache__/write.cpython-38.pyc
test/test_copyright.py test/test_copyright.py
test/test_flake8.py test/test_flake8.py
test/test_pep257.py test/test_pep257.py
\ No newline at end of file
File added
from .projection import PyProjectionGeometry, PyProjection from .projection import PyProjectionGeometry, PyProjection
from .region_of_intrest import PyRegionOfIntrest from .volume import PyVolume, PyRegionOfIntrest
\ No newline at end of file \ No newline at end of file
No preview for this file type
No preview for this file type
No preview for this file type
import numpy as np import numpy as np
import json import json
from pathlib import Path from pathlib import Path
from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest, PyVolume
class BaseDataLoader(): class BaseDataLoader():
def __init__(self, def __init__(self,
porjection_geometry_suffix: str, porjection_geometry_suffix: str,
projection_suffix: str, projection_suffix: str,
region_of_intrest_suffix: str): region_of_intrest_suffix: str,
volume_suffix: str):
self.porjection_geometry_suffix = porjection_geometry_suffix self.porjection_geometry_suffix = porjection_geometry_suffix
self.projection_suffix = projection_suffix self.projection_suffix = projection_suffix
self.region_of_intrest_suffix = region_of_intrest_suffix self.region_of_intrest_suffix = region_of_intrest_suffix
self.volume_suffix = volume_suffix
def load_projection_geometry(self, load_path: Path) -> PyProjectionGeometry: def load_projection_geometry(self, load_path: Path) -> PyProjectionGeometry:
raise NotImplementedError raise NotImplementedError()
def load_projection(self, load_path: Path) -> PyProjection: def load_projection(self, load_path: Path) -> PyProjection:
raise NotImplementedError raise NotImplementedError()
def load_region_of_intrest(self, load_path: Path) -> PyRegionOfIntrest: def load_region_of_intrest(self, load_path: Path) -> PyRegionOfIntrest:
raise NotImplementedError raise NotImplementedError()
def load_volume(self, load_path: Path) -> PyVolume:
raise NotImplementedError()
\ No newline at end of file
No preview for this file type
File added
File added
...@@ -2,13 +2,15 @@ import numpy as np ...@@ -2,13 +2,15 @@ import numpy as np
import json import json
from pathlib import Path from pathlib import Path
from ..loader import BaseDataLoader, PyProjection, PyProjectionGeometry, PyRegionOfIntrest from ..loader import BaseDataLoader, PyProjection, PyProjectionGeometry, PyRegionOfIntrest, PyVolume
from PIL import Image from PIL import Image
import xtiff
import pyometiff
class RqJsonLoader(BaseDataLoader): class RqJsonLoader(BaseDataLoader):
def __init__(self): def __init__(self):
super().__init__('.geom-json', '.tif', '.roi-json') super().__init__('.geom-json', '.tif', '.roi-json', '.ome.tiff')
def load_json(self, load_path: Path) -> dict: def load_json(self, load_path: Path) -> dict:
with open(str(load_path), 'r') as f: with open(str(load_path), 'r') as f:
...@@ -51,9 +53,25 @@ class RqJsonLoader(BaseDataLoader): ...@@ -51,9 +53,25 @@ class RqJsonLoader(BaseDataLoader):
data_dict = self.load_json(load_path) data_dict = self.load_json(load_path)
region_of_intrest = PyRegionOfIntrest( region_of_intrest = PyRegionOfIntrest(
start_point_mm=np.array(data_dict['start_point_mm']), center_points_mm=np.array(data_dict['center_points_mm']),
end_point_mm=np.array(data_dict['end_point_mm']), dimensions_mm=np.array(data_dict['dimensions_mm']),
frame_id=data_dict['resoluion_mm'], frame_id=data_dict['frame_id'],
resoluion_mm=np.array(data_dict['resoluion_mm'])) resolution_mm=np.array(data_dict['resolution_mm']))
return region_of_intrest return region_of_intrest
def load_volume(self, load_path: Path) -> PyVolume:
load_path_roi = load_path.parent / f'{load_path.stem}{self.region_of_intrest_suffix}'
roi = self.load_region_of_intrest(load_path_roi)
reader = pyometiff.OMETIFFReader(load_path)
volume, _, _ = reader.read()
if volume.dtype == np.uint16:
data_type = 0
elif volume.dtype == np.uint8:
data_type = 1
else:
raise ValueError('data type not implemented.')
return PyVolume(volume, roi, data_type)
...@@ -2,13 +2,15 @@ import numpy as np ...@@ -2,13 +2,15 @@ import numpy as np
import json import json
from pathlib import Path from pathlib import Path
from ..writer import BaseDataWriter, PyProjection, PyProjectionGeometry, PyRegionOfIntrest from ..writer import BaseDataWriter, PyProjection, PyProjectionGeometry, PyRegionOfIntrest, PyVolume
from PIL import Image from PIL import Image
import xtiff
import pyometiff
class RqJsonWriter(BaseDataWriter): class RqJsonWriter(BaseDataWriter):
def __init__(self): def __init__(self):
super().__init__('.geom-json', '.tif', '.roi-json') super().__init__('.geom-json', '.tif', '.roi-json', '.ome.tiff')
def write_json(self, save_path: Path, data_dict: dict): def write_json(self, save_path: Path, data_dict: dict):
with open(str(save_path), 'w') as f: with open(str(save_path), 'w') as f:
...@@ -61,6 +63,12 @@ class RqJsonWriter(BaseDataWriter): ...@@ -61,6 +63,12 @@ class RqJsonWriter(BaseDataWriter):
data_dict['center_points_mm'] = region_of_intrest.center_points_mm.tolist() data_dict['center_points_mm'] = region_of_intrest.center_points_mm.tolist()
data_dict['dimensions_mm'] = region_of_intrest.dimensions_mm.tolist() data_dict['dimensions_mm'] = region_of_intrest.dimensions_mm.tolist()
data_dict['frame_id'] = region_of_intrest.frame_id data_dict['frame_id'] = region_of_intrest.frame_id
data_dict['resoluion_mm'] = region_of_intrest.resolution_mm.tolist() data_dict['resolution_mm'] = region_of_intrest.resolution_mm.tolist()
self.write_json(save_path, data_dict) self.write_json(save_path, data_dict)
def write_volume(self, save_path: Path, volume: PyVolume):
save_path_region_of_intrest = save_path.parent / f'{save_path.stem}{self.region_of_intrest_suffix}'
self.write_region_of_intrest(save_path_region_of_intrest, volume.roi)
writer = pyometiff.OMETIFFWriter(save_path, volume.array, dict())
writer.write()
\ No newline at end of file
import numpy as np import numpy as np
import json import json
from pathlib import Path from pathlib import Path
from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest from ...common import PyProjectionGeometry, PyProjection, PyRegionOfIntrest, PyVolume
class BaseDataWriter(): class BaseDataWriter():
projection_name: str = 'projection' projection_name: str = 'projection'
projection_geometry_name: str ='geometry' projection_geometry_name: str ='geometry'
region_of_intrest_name: str = 'roi' region_of_intrest_name: str = 'roi'
volume_name: str = 'volume'
def __init__(self, def __init__(self,
porjection_geometry_suffix: str, porjection_geometry_suffix: str,
projection_suffix: str, projection_suffix: str,
region_of_intrest_suffix: str): region_of_intrest_suffix: str,
volume_suffix: str):
self.porjection_geometry_suffix = porjection_geometry_suffix self.porjection_geometry_suffix = porjection_geometry_suffix
self.projection_suffix = projection_suffix self.projection_suffix = projection_suffix
self.region_of_intrest_suffix = region_of_intrest_suffix self.region_of_intrest_suffix = region_of_intrest_suffix
self.volume_suffix = volume_suffix
def write_projection_geometry(self, save_path: Path, projection_geometry: PyProjectionGeometry): def write_projection_geometry(self, save_path: Path, projection_geometry: PyProjectionGeometry):
raise NotImplementedError raise NotImplementedError()
def write_projection(self, save_path: Path, projection: PyProjection): def write_projection(self, save_path: Path, projection: PyProjection):
raise NotImplementedError raise NotImplementedError()
def write_region_of_intrest(self, save_path: Path, region_of_intrest: PyRegionOfIntrest): def write_region_of_intrest(self, save_path: Path, region_of_intrest: PyRegionOfIntrest):
raise NotImplementedError raise NotImplementedError
def write_volume(save_path: Path, region_of_intrest: PyRegionOfIntrest):
raise NotImplementedError()
def get_next_projection_save_path(self, save_folder: Path) -> Path: def get_next_projection_save_path(self, save_folder: Path) -> Path:
return self.get_projection_save_path_i(save_folder, self.number_of_projections(save_folder) + 1) return self.get_projection_save_path_i(save_folder, self.number_of_projections(save_folder) + 1)
...@@ -45,6 +50,12 @@ class BaseDataWriter(): ...@@ -45,6 +50,12 @@ class BaseDataWriter():
def get_region_of_intrest_save_path_i(self, save_folder: Path, i) -> Path: def get_region_of_intrest_save_path_i(self, save_folder: Path, i) -> Path:
return save_folder / f'{self.region_of_intrest_name}_{i:05}{self.region_of_intrest_suffix}' return save_folder / f'{self.region_of_intrest_name}_{i:05}{self.region_of_intrest_suffix}'
def get_next_volume_save_path(self, save_folder: Path) -> Path:
return self.get_volume_save_path_i(save_folder, self.number_of_volumes(save_folder) + 1)
def get_volume_save_path_i(self, save_folder: Path, i) -> Path:
return save_folder / f'{self.volume_name}_{i:05}{self.volume_suffix}'
def number_of_projection_geometries(self, folder: Path) -> int: def number_of_projection_geometries(self, folder: Path) -> int:
return len(list(folder.glob(f'{self.projection_geometry_name}*{self.porjection_geometry_suffix}'))) return len(list(folder.glob(f'{self.projection_geometry_name}*{self.porjection_geometry_suffix}')))
...@@ -53,3 +64,6 @@ class BaseDataWriter(): ...@@ -53,3 +64,6 @@ class BaseDataWriter():
def number_of_region_of_intrests(self, folder: Path) -> int: def number_of_region_of_intrests(self, folder: Path) -> int:
return len(list(folder.glob(f'{self.region_of_intrest_name}*{self.region_of_intrest_suffix}'))) return len(list(folder.glob(f'{self.region_of_intrest_name}*{self.region_of_intrest_suffix}')))
def number_of_volumes(self, folder: Path) -> int:
return len(list(folder.glob(f'{self.volume_name}*{self.volume_suffix}')))
...@@ -114,6 +114,13 @@ class PyProjection(PyProjectionGeometry): ...@@ -114,6 +114,13 @@ class PyProjection(PyProjectionGeometry):
return message return message
def get_projection_geometry(self) -> PyProjectionGeometry:
return PyProjectionGeometry(self.focal_spot_mm,
self.detector_postion_mm,
self.detector_orientation_quad,
self.frame_id,
self.focal_spot_orientation_quad)
@property @property
def detector_heigth_px(self) -> int: def detector_heigth_px(self) -> int:
return self.image.shape[0] return self.image.shape[0]
...@@ -128,4 +135,4 @@ class PyProjection(PyProjectionGeometry): ...@@ -128,4 +135,4 @@ class PyProjection(PyProjectionGeometry):
@property @property
def pixel_pitch_y_mm(self) -> float: def pixel_pitch_y_mm(self) -> float:
return self.detector_heigth_mm / self.detector_heigth_px return self.detector_heigth_mm / self.detector_heigth_px
\ No newline at end of file
...@@ -7,15 +7,15 @@ from visualization_msgs.msg import Marker ...@@ -7,15 +7,15 @@ from visualization_msgs.msg import Marker
class PyRegionOfIntrest(): class PyRegionOfIntrest():
def __init__(self, center_points_mm: np.ndarray, dimensions_mm: np.ndarray, frame_id: str = 'object', def __init__(self, center_points_mm: np.ndarray, dimensions_mm: np.ndarray, frame_id: str = 'object',
resolution_mm: np.ndarray = np.array([0.1, 0.1, 0.1])): resolution_mm: np.ndarray = np.array([0.1, 0.1, 0.1])):
self.center_points_mm = center_points_mm self.center_points_mm = center_points_mm.reshape((-1, 3))
self.dimensions_mm = dimensions_mm self.dimensions_mm = dimensions_mm.reshape((-1, 3))
self.frame_id = frame_id self.frame_id = frame_id
self.resolution_mm = resolution_mm self.resolution_mm = resolution_mm.reshape((-1, 3))
@classmethod @classmethod
def dummy(cls): def dummy(cls):
return cls((np.random.random((3, )) - 0.5) * 20., return cls((np.random.random((3, )) - 0.5) * 20.,
(np.random.random((3, )) - 0.5) * 10.) np.random.random((3, )) * 10.)
@classmethod @classmethod
def from_message(cls, msg: RegionOfIntrest): def from_message(cls, msg: RegionOfIntrest):
...@@ -45,6 +45,12 @@ class PyRegionOfIntrest(): ...@@ -45,6 +45,12 @@ class PyRegionOfIntrest():
def number_of_rois(self) -> int: def number_of_rois(self) -> int:
return self.center_points_mm.shape[0] return self.center_points_mm.shape[0]
@property
def shape(self) -> tuple:
shape = self.dimensions_mm[0] // self.resolution_mm[0]
return (int(shape[0]), int(shape[1]), int(shape[2]))
def as_message(self) -> RegionOfIntrest: def as_message(self) -> RegionOfIntrest:
message = RegionOfIntrest() message = RegionOfIntrest()
roi_list = list() roi_list = list()
...@@ -52,13 +58,13 @@ class PyRegionOfIntrest(): ...@@ -52,13 +58,13 @@ class PyRegionOfIntrest():
for i in range(self.number_of_rois): for i in range(self.number_of_rois):
roi = Marker() roi = Marker()
roi.pose.position.x = self.center_points_mm[i][0] roi.pose.position.x = float(self.center_points_mm[i][0])
roi.pose.position.y = self.center_points_mm[i][1] roi.pose.position.y = float(self.center_points_mm[i][1])
roi.pose.position.z = self.center_points_mm[i][2] roi.pose.position.z = float(self.center_points_mm[i][2])
roi.scale.x = self.dimensions_mm[i][0] roi.scale.x = float(self.dimensions_mm[i][0])
roi.scale.y = self.dimensions_mm[i][1] roi.scale.y = float(self.dimensions_mm[i][1])
roi.scale.z = self.dimensions_mm[i][2] roi.scale.z = float(self.dimensions_mm[i][2])
roi.header.frame_id = self.frame_id roi.header.frame_id = self.frame_id
...@@ -66,8 +72,8 @@ class PyRegionOfIntrest(): ...@@ -66,8 +72,8 @@ class PyRegionOfIntrest():
message.region_of_intrest_stack.markers = roi_list message.region_of_intrest_stack.markers = roi_list
message.resolution.x = self.resolution_mm[0] message.resolution.x = float(self.resolution_mm[0][0])
message.resolution.x = self.resolution_mm[1] message.resolution.y = float(self.resolution_mm[0][1])
message.resolution.x = self.resolution_mm[2] message.resolution.z = float(self.resolution_mm[0][2])
return message return message
from numpy import ndarray
from numpy.core.multiarray import array as array
from rq_interfaces.msg import Volume
from visualization_msgs.msg import Marker
from .region_of_intrest import PyRegionOfIntrest
from sensor_msgs.msg import Image
from enum import IntEnum
import numpy as np
import ros2_numpy
class VOLUME_TYPES(IntEnum):
UINT_16 = 0
UINT_8 = 1
class PyVolume():
def __init__(self, array: ndarray, roi: PyRegionOfIntrest, data_type: VOLUME_TYPES = ...):
self.roi = roi
self.array = array
self.data_typ = data_type
@staticmethod
def get_data_type(volume_type: VOLUME_TYPES) -> np.dtype:
if volume_type == VOLUME_TYPES.UINT_16:
return np.uint16
elif volume_type == VOLUME_TYPES.UINT_8:
return np.uint8
else:
raise ValueError('Datatype is not implemented')
@staticmethod
def enum_to_numpify(volume_type: VOLUME_TYPES) -> str:
if volume_type == VOLUME_TYPES.UINT_16:
return 'mono16'
elif volume_type == VOLUME_TYPES.UINT_8:
return 'mono8'
else:
raise ValueError('Datatype is not implemented')
@classmethod
def dummy(cls):
roi = PyRegionOfIntrest.dummy()
array = np.random.randint(0, 255, size=roi.shape)
data_type = VOLUME_TYPES.UINT_8
return cls(array, roi, data_type)
@classmethod
def from_message(cls, msg: Volume):
roi: Marker = msg.grid.region_of_intrest_stack.markers[0]
center_points_mm = np.array([
roi.pose.position.x,
roi.pose.position.y,
roi.pose.position.z])
dimensions_mm = np.array([
roi.scale.x,
roi.scale.y,
roi.scale.z])
frame_id = roi.header.frame_id
resolution_mm = np.array([
msg.grid.resolution.x,
msg.grid.resolution.y,
msg.grid.resolution.z])
py_roi = PyRegionOfIntrest(center_points_mm, dimensions_mm, frame_id, resolution_mm)
data_typ = msg.datatype
array = np.zeros(py_roi.shape, dtype=cls.get_data_type(data_typ))
for i, slice in enumerate(msg.slices):
array[:, :, i] = ros2_numpy.numpify(slice).reshape((py_roi.shape[0], py_roi.shape[1])).astype(cls.get_data_type(data_typ))
return cls(array, py_roi, data_typ)
def as_message(self) -> Volume:
message = Volume()
message.datatype = self.data_typ
message.grid = self.roi.as_message()
message.slices = list()
for z in range(self.array.shape[2]):
message.slices.append(
ros2_numpy.msgify(Image,
self.array[:, :, z].astype(self.get_data_type(self.data_typ)),
self.enum_to_numpify(self.data_typ)))
return message
@property
def shape(self):
return self.array.shape
...@@ -51,6 +51,7 @@ class WorkflowNode(Node): ...@@ -51,6 +51,7 @@ class WorkflowNode(Node):
reached, cost, _ = self.hardware_interface.reachability_response_2_py(response) reached, cost, _ = self.hardware_interface.reachability_response_2_py(response)
return reached, cost return reached, cost
def main(): def main():
rclpy.init() rclpy.init()
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment