Skip to content

Commit

Permalink
fix some import issue
Browse files Browse the repository at this point in the history
  • Loading branch information
icrdr committed Jul 23, 2024
1 parent 7787888 commit c9a961d
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 100 deletions.
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@

# Bioxel Nodes

![Static Badge](https://img.shields.io/badge/Blender-orange?style=for-the-badge&logo=blender&logoColor=white)
![GitHub License](https://img.shields.io/github/license/OmooLab/BioxelNodes?style=for-the-badge)
![GitHub Release](https://img.shields.io/github/v/release/OmooLab/BioxelNodes?style=for-the-badge)
![GitHub Repo stars](https://img.shields.io/github/stars/OmooLab/BioxelNodes?style=for-the-badge)
![Static Badge](https://img.shields.io/badge/Blender-orange?style=for-the-badge&logo=blender&logoColor=white&color=black)
![GitHub License](https://img.shields.io/github/license/OmooLab/BioxelNodes?style=for-the-badge&labelColor=black)
![GitHub Release](https://img.shields.io/github/v/release/OmooLab/BioxelNodes?style=for-the-badge&labelColor=black)
![GitHub Repo stars](https://img.shields.io/github/stars/OmooLab/BioxelNodes?style=for-the-badge&labelColor=black)

![Discord](https://img.shields.io/discord/1265129134397587457?style=for-the-badge&logo=discord&label=Discord&labelColor=white&color=black&link=https%3A%2F%2Fdiscord.gg%2FpYkNyq2TjE)

Bioxel Nodes is a Blender addon for scientific volumetric data visualization. It using Blender's powerful **Geometry Nodes** and **Cycles** to process and render volumetric data.

![cover](https://omoolab.github.io/BioxelNodes/latest/assets/cover.png)

- Fantastic rendering result, also support EEVEE NEXT
- Support multiple formats
- Support 4D volumetric data
- All kinds of cutters
- Simple and powerful nodes
- Fantastic rendering result, also support EEVEE NEXT.
- Support multiple formats.
- Support 4D volumetric data.
- All kinds of cutters.
- Simple and powerful nodes.
- Based on blender natively, can work without addon.

**Click [Getting Started](https://omoolab.github.io/BioxelNodes/latest/installation) to begin your journey into visualizing volumetric data!**
**Read the [getting started](https://omoolab.github.io/BioxelNodes/latest/installation) to begin your journey into visualizing volumetric data!**

Welcome to our [discord server](https://discord.gg/pYkNyq2TjE), if you have any problems with this add-on.

## Support Multiple Formats

Expand Down Expand Up @@ -55,7 +59,7 @@ Bioxel Nodes is a Blender addon for scientific volumetric data visualization. It
- Only works with Cycles CPU , Cycles GPU (OptiX), EEVEE
- Section surface cannot be generated when convert to mesh (will be supported soon)

## Compatibile to Newer Version
## Compatible to Newer Version

**Updating this addon may break old files, so read the following carefully before updating**

Expand Down
25 changes: 18 additions & 7 deletions bioxelnodes/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ def progress_callback(factor, text):
progress_callback=progress_callback)
except CancelledByUser:
return
except RuntimeError:
self.has_error = True
return

if cancel():
return
Expand All @@ -183,6 +186,8 @@ def progress_callback(factor, text):

# Init cancel flag
self.is_cancelled = False
self.has_error = False

# Create the thread
self.thread = threading.Thread(target=parse_volumetric_data_func,
args=(self, context, lambda: self.is_cancelled))
Expand Down Expand Up @@ -229,6 +234,11 @@ def modal(self, context, event):
self.report({"WARNING"}, "Canncelled by user.")
return {'CANCELLED'}

# Check if thread is cancelled by user
if self.has_error:
self.report({"ERROR"}, "Fail to parse, something went wrong.")
return {'CANCELLED'}

# If not canncelled...
for key, value in self.meta.items():
print(f"{key}: {value}")
Expand All @@ -239,9 +249,12 @@ def modal(self, context, event):
orig_spacing[1], orig_spacing[2])
bioxel_size = max(min_size, 1.0)

layer_size = get_layer_size(orig_shape,
layer_shape = get_layer_shape(1, orig_shape, orig_spacing)
layer_size = get_layer_size(layer_shape,
bioxel_size)
log10 = math.floor(math.log10(max(*layer_size)))
log10 = max(1,log10)
log10 = min(3,log10)
scene_scale = math.pow(10, -log10)

if self.container:
Expand Down Expand Up @@ -700,6 +713,7 @@ def modal(self, context, event):

container.matrix_world = mat_ras2blender @ mat_scene_scale
container.name = container_name
container.show_in_front = True
container.data.name = container_name

container['bioxel_container'] = True
Expand Down Expand Up @@ -741,8 +755,8 @@ def modal(self, context, event):
layer.hide_select = True
layer.hide_render = True
layer.hide_viewport = True
layer.data.display.use_slice = True
layer.data.display.density = 1e-05
# layer.data.display.use_slice = True
# layer.data.display.density = 1e-05

layer['bioxel_layer'] = True
layer['bioxel_layer_type'] = layer_info['type']
Expand Down Expand Up @@ -811,11 +825,8 @@ def modal(self, context, event):

# Change render setting for better result
preferences = context.preferences.addons[__package__].preferences

if preferences.do_change_render_setting and self.is_first_import:
if bpy.app.version < (4, 2, 0):
bpy.context.scene.render.engine = 'CYCLES'

bpy.context.scene.render.engine = 'CYCLES'
try:
bpy.context.scene.cycles.shading_system = True
bpy.context.scene.cycles.volume_bounces = 12
Expand Down
4 changes: 2 additions & 2 deletions bioxelnodes/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ def deep_copy_layer(vdb_path, base_layer, name):
copyed_layer.hide_select = True
copyed_layer.hide_render = True
copyed_layer.hide_viewport = True
copyed_layer.data.display.use_slice = True
copyed_layer.data.display.density = 1e-05
# copyed_layer.data.display.use_slice = True
# copyed_layer.data.display.density = 1e-05

copyed_layer['bioxel_layer'] = True
copyed_layer['bioxel_layer_type'] = base_layer['bioxel_layer_type']
Expand Down
177 changes: 108 additions & 69 deletions bioxelnodes/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
except:
...

"""
Convert any volumetric data to 3D numpy array with order TXYZC
"""

SUPPORT_EXTS = ['', '.dcm', '.DCM', '.DICOM', '.ima', '.IMA',
'.bmp', '.BMP',
Expand Down Expand Up @@ -136,7 +139,11 @@ def parse_volumetric_data(filepath: str, series_id="", progress_callback=None):
if len(sequence) > 1:
is_sequence = True

if ext in MRC_EXTS and not is_sequence:
volume = None

# Parsing with mrcfile
if volume is None and ext in MRC_EXTS and not is_sequence:
print("Parsing with mrcfile...")
# TODO: much to do with mrc
with mrcfile.open(filepath) as mrc:
volume = mrc.data
Expand All @@ -159,10 +166,10 @@ def parse_volumetric_data(filepath: str, series_id="", progress_callback=None):

elif mrc.is_volume_stack():
volume = np.expand_dims(volume, axis=-1) # expend channel

name = Path(filepath).name.removesuffix(ext).replace(" ", "-")
shape = volume.shape[1:4]
spacing = (mrc.voxel_size.x, mrc.voxel_size.y, mrc.voxel_size.z)

meta = {
"name": name,
"description": "",
Expand All @@ -175,7 +182,9 @@ def parse_volumetric_data(filepath: str, series_id="", progress_callback=None):
"is_oriented": False
}

elif ext in OME_EXTS and not is_sequence:
# Parsing with OMETIFFReader
if volume is None and ext in OME_EXTS and not is_sequence:
print("Parsing with OMETIFFReader...")
reader = OMETIFFReader(fpath=filepath)
ome_volume, metadata, xml_metadata = reader.read()

Expand All @@ -189,69 +198,70 @@ def parse_volumetric_data(filepath: str, series_id="", progress_callback=None):
# for key in metadata:
# print(f"{key},{metadata[key]}")
ome_order = metadata['DimOrder BF Array']
except:
ome_order = "TCZYX"

if ome_volume.ndim == 2:
ome_order = ome_order.replace("T", "")\
.replace("C", "").replace("Z", "")
bioxel_order = (ome_order.index('X'),
ome_order.index('Y'))
volume = np.transpose(ome_volume, bioxel_order)
volume = np.expand_dims(volume, axis=0) # expend frame
volume = np.expand_dims(volume, axis=-1) # expend Z
volume = np.expand_dims(volume, axis=-1) # expend channel

elif ome_volume.ndim == 3:
# -> XYZC
ome_order = ome_order.replace("T", "").replace("C", "")
bioxel_order = (ome_order.index('X'),
ome_order.index('Y'),
ome_order.index('Z'))
volume = np.transpose(ome_volume, bioxel_order)
volume = np.expand_dims(volume, axis=0) # expend frame
volume = np.expand_dims(volume, axis=-1) # expend channel
elif ome_volume.ndim == 4:
# -> XYZC
ome_order = ome_order.replace("T", "")
bioxel_order = (ome_order.index('X'),
ome_order.index('Y'),
ome_order.index('Z'),
ome_order.index('C'))
volume = np.transpose(ome_volume, bioxel_order)
volume = np.expand_dims(volume, axis=0) # expend frame
elif ome_volume.ndim == 5:
# -> TXYZC
bioxel_order = (ome_order.index('T'),
ome_order.index('X'),
ome_order.index('Y'),
ome_order.index('Z'),
ome_order.index('C'))
volume = np.transpose(ome_volume, bioxel_order)
if ome_volume.ndim == 2:
ome_order = ome_order.replace("T", "")\
.replace("C", "").replace("Z", "")
bioxel_order = (ome_order.index('X'),
ome_order.index('Y'))
volume = np.transpose(ome_volume, bioxel_order)
volume = np.expand_dims(volume, axis=0) # expend frame
volume = np.expand_dims(volume, axis=-1) # expend Z
volume = np.expand_dims(volume, axis=-1) # expend channel

elif ome_volume.ndim == 3:
# -> XYZC
ome_order = ome_order.replace("T", "").replace("C", "")
bioxel_order = (ome_order.index('X'),
ome_order.index('Y'),
ome_order.index('Z'))
volume = np.transpose(ome_volume, bioxel_order)
volume = np.expand_dims(volume, axis=0) # expend frame
volume = np.expand_dims(volume, axis=-1) # expend channel
elif ome_volume.ndim == 4:
# -> XYZC
ome_order = ome_order.replace("T", "")
bioxel_order = (ome_order.index('X'),
ome_order.index('Y'),
ome_order.index('Z'),
ome_order.index('C'))
volume = np.transpose(ome_volume, bioxel_order)
volume = np.expand_dims(volume, axis=0) # expend frame
elif ome_volume.ndim == 5:
# -> TXYZC
bioxel_order = (ome_order.index('T'),
ome_order.index('X'),
ome_order.index('Y'),
ome_order.index('Z'),
ome_order.index('C'))
volume = np.transpose(ome_volume, bioxel_order)

shape = volume.shape[1:4]

shape = volume.shape[1:4]
try:
spacing = (metadata['PhysicalSizeX'],
metadata['PhysicalSizeY'],
metadata['PhysicalSizeZ'])
except:
spacing = (1, 1, 1)

try:
spacing = (metadata['PhysicalSizeX'],
metadata['PhysicalSizeY'],
metadata['PhysicalSizeZ'])
name = Path(filepath).name.removesuffix(ext).replace(" ", "-")
meta = {
"name": name,
"description": "",
"shape": shape,
"spacing": spacing,
"origin": (0, 0, 0),
"direction": (1, 0, 0, 0, 1, 0, 0, 0, 1),
"frame_count": volume.shape[0],
"channel_count": volume.shape[-1],
"is_oriented": False
}
except:
spacing = (1, 1, 1)

name = Path(filepath).name.removesuffix(ext).replace(" ", "-")
meta = {
"name": name,
"description": "",
"shape": shape,
"spacing": spacing,
"origin": (0, 0, 0),
"direction": (1, 0, 0, 0, 1, 0, 0, 0, 1),
"frame_count": volume.shape[0],
"channel_count": volume.shape[-1],
"is_oriented": False
}
...

else:
# Parsing with SimpleITK
if volume is None:
print("Parsing with SimpleITK...")
if ext in DICOM_EXTS:
dir_path = Path(filepath).resolve().parent
reader = sitk.ImageSeriesReader()
Expand Down Expand Up @@ -294,10 +304,13 @@ def get_meta(key):
name = name.replace(" ", "-")
description = description.replace(" ", "-")

elif ext in SEQUENCE_EXTS:
itk_volume = sitk.ReadImage(sequence)
name = get_sequence_name(filepath).replace(" ", "-")
description = ""
elif ext in SEQUENCE_EXTS and is_sequence:
try:
itk_volume = sitk.ReadImage(sequence)
name = get_sequence_name(filepath).replace(" ", "-")
description = ""
except RuntimeError as e:
raise e
else:
itk_volume = sitk.ReadImage(filepath)
name = Path(filepath).name.removesuffix(ext).replace(" ", "-")
Expand All @@ -311,7 +324,33 @@ def get_meta(key):
if not progressing:
raise CancelledByUser

if itk_volume.GetDimension() == 3:
if itk_volume.GetDimension() == 2:
volume = sitk.GetArrayFromImage(itk_volume)

if volume.ndim == 3:
volume = np.transpose(volume, (1, 0, 2))

volume = np.expand_dims(volume, axis=-2) # expend Z
else:
volume = np.transpose(volume)
volume = np.expand_dims(volume, axis=-1) # expend Z
volume = np.expand_dims(volume, axis=-1) # expend channel

volume = np.expand_dims(volume, axis=0) # expend frame

meta = {
"name": name,
"description": description,
"shape": volume.shape[1:4],
"spacing": (1, 1, 1),
"origin": (0, 0, 0),
"direction": (1, 0, 0, 0, 1, 0, 0, 0, 1),
"frame_count": 1,
"channel_count": volume.shape[-1],
"is_oriented": False
}

elif itk_volume.GetDimension() == 3:
itk_volume = sitk.DICOMOrient(itk_volume, 'RAS')

volume = sitk.GetArrayFromImage(itk_volume)
Expand All @@ -332,12 +371,12 @@ def get_meta(key):
"spacing": tuple(itk_volume.GetSpacing()),
"origin": tuple(itk_volume.GetOrigin()),
"direction": tuple(itk_volume.GetDirection()),
"frame_count": volume.shape[0],
"frame_count": 1,
"channel_count": volume.shape[-1],
"is_oriented": True
}

if itk_volume.GetDimension() == 4:
elif itk_volume.GetDimension() == 4:
# FIXME: not sure...
direction = np.array(itk_volume.GetDirection())
direction = direction.reshape(3, 3) if itk_volume.GetDimension() == 3 \
Expand Down
Loading

0 comments on commit c9a961d

Please sign in to comment.