Skip to content

Commit

Permalink
Merge pull request #36 from OmooLab/feature/dev
Browse files Browse the repository at this point in the history
Feature/dev
  • Loading branch information
icrdr authored Aug 5, 2024
2 parents 088d9c1 + 2563f39 commit 2429eef
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 70 deletions.
29 changes: 17 additions & 12 deletions bioxelnodes/bioxel/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@
"""

SUPPORT_EXTS = ['', '.dcm', '.DCM', '.DICOM', '.ima', '.IMA',
'.ome.tiff', '.ome.tif',
'.tif', '.TIF', '.tiff', '.TIFF',
'.mrc', '.mrc.gz', '.map', '.map.gz',
'.bmp', '.BMP',
'.png', '.PNG',
'.jpg', '.JPG', '.jpeg', '.JPEG',
'.PIC', '.pic',
'.gipl', '.gipl.gz',
'.jpg', '.JPG', '.jpeg', '.JPEG',
'.lsm', '.LSM',
'.tif', '.TIF', '.tiff', '.TIFF',
'.mnc', '.MNC',
'.mrc', '.rec',
'.mha', '.mhd',
'.hdf', '.h4', '.hdf4', '.he2', '.h5', '.hdf5', '.he5',
'.nia', '.nii', '.nii.gz', '.hdr', '.img', '.img.gz',
'.nrrd', '.nhdr',
'.png', '.PNG',
'.vtk',
'.ome.tiff', '.ome.tif',
'.mrc', '.mrc.gz', '.map', '.map.gz']
'.gz']

OME_EXTS = ['.ome.tiff', '.ome.tif',
'.tif', '.TIF', '.tiff', '.TIFF']
Expand Down Expand Up @@ -68,7 +69,7 @@ def get_file_name(filepath: Path):
return filepath.name.removesuffix(ext).replace(" ", "-")


def get_file_index(filepath: Path):
def get_file_number(filepath: Path) -> str:
name = get_file_name(filepath)
digits = ""

Expand All @@ -85,15 +86,18 @@ def get_file_index(filepath: Path):
break

# Reverse the digits string to get the correct order
last_number = digits[::-1]

return int(last_number) if last_number != "" else 0
return digits[::-1]


def get_sequence_name(filepath: Path) -> str:
name = get_file_name(filepath)
index = get_file_index(filepath)
return name.removesuffix(str(index))
number = get_file_number(filepath)
return name.removesuffix(number)


def get_sequence_index(filepath: Path) -> int:
number = get_file_number(filepath)
return int(number) if number != "" else 0


def collect_sequence(filepath: Path):
Expand All @@ -102,9 +106,10 @@ def collect_sequence(filepath: Path):
if f.is_file() \
and get_ext(filepath) == get_ext(f) \
and get_sequence_name(filepath) == get_sequence_name(f):
index = get_file_index(f)
index = get_sequence_index(f)
file_dict[index] = f

# reomve isolated seq file
for key in file_dict.copy().keys():
if not file_dict.get(key+1) \
and not file_dict.get(key-1):
Expand Down
2 changes: 1 addition & 1 deletion bioxelnodes/blender_manifest.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
schema_version = "1.0.0"

id = "bioxelnodes"
version = "0.3.1"
version = "0.3.2"
name = "Bioxel Nodes"
tagline = "For scientific volumetric data visualization in Blender"
maintainer = "Ma Nan <icrdr2010@outlook.com>"
Expand Down
103 changes: 53 additions & 50 deletions bioxelnodes/operators/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,6 @@
import SimpleITK as sitk
import transforms3d

FH_EXTS = ['', '.dcm', '.DCM', '.DICOM', '.ima', '.IMA',
'.gipl', '.gipl.gz',
'.mnc', '.MNC',
'.mrc', '.rec',
'.mha', '.mhd',
'.nia', '.nii', '.nii.gz', '.hdr', '.img', '.img.gz',
'.hdf', '.h4', '.hdf4', '.he2', '.h5', '.hdf5', '.he5',
'.nrrd', '.nhdr',
'.vtk',
'.gz'
'.ome.tiff', '.ome.tif',
'.mrc', '.mrc.gz', '.map', '.map.gz']


def get_layer_shape(bioxel_size: float, orig_shape: tuple, orig_spacing: tuple):
shape = (int(orig_shape[0] / bioxel_size * orig_spacing[0]),
Expand Down Expand Up @@ -69,24 +56,20 @@ class ImportVolumetricData():
bl_options = {'UNDO'}

filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
directory: bpy.props.StringProperty(subtype='DIR_PATH') # type: ignore

read_as = "scalar"

def execute(self, context):
container_objs = get_container_objs_from_selection()
data_path = Path(self.filepath).resolve()
ext = get_ext(data_path)
if ext not in SUPPORT_EXTS:
self.report({"WARNING"}, "Not supported format.")
return {'CANCELLED'}

if len(container_objs) > 0:
bpy.ops.bioxelnodes.parse_volumetric_data('INVOKE_DEFAULT',
filepath=self.filepath,
directory=self.directory,
container_obj_name=container_objs[0].name,
read_as=self.read_as)
else:
bpy.ops.bioxelnodes.parse_volumetric_data('INVOKE_DEFAULT',
filepath=self.filepath,
directory=self.directory,
read_as=self.read_as)
bpy.ops.bioxelnodes.parse_volumetric_data('INVOKE_DEFAULT',
filepath=self.filepath,
skip_read_as=True,
read_as=self.read_as)

return {'FINISHED'}

Expand All @@ -113,7 +96,7 @@ class BIOXELNODES_FH_ImportVolumetricData(bpy.types.FileHandler):
bl_idname = "BIOXELNODES_FH_ImportVolumetricData"
bl_label = "File handler for dicom import"
bl_import_operator = "bioxelnodes.parse_volumetric_data"
bl_file_extensions = ";".join(FH_EXTS)
bl_file_extensions = ";".join(SUPPORT_EXTS)

@classmethod
def poll_drop(cls, context):
Expand All @@ -135,21 +118,26 @@ def get_series_ids(self, context):

class ParseVolumetricData(bpy.types.Operator):
bl_idname = "bioxelnodes.parse_volumetric_data"
bl_label = "Import Volumetric Data"
bl_label = "Import Volumetric Data (BioxelNodes)"
bl_description = "Import Volumetric Data as Layer"
bl_options = {'UNDO'}

meta = None
thread = None
_timer = None
container_obj_name = ""

progress: bpy.props.FloatProperty(name="Progress",
options={"SKIP_SAVE"},
default=1) # type: ignore

filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
directory: bpy.props.StringProperty(subtype='DIR_PATH') # type: ignore
container_obj_name: bpy.props.StringProperty() # type: ignore

skip_read_as: bpy.props.BoolProperty(name="Skip Read As",
default=False) # type: ignore

skip_series_select: bpy.props.BoolProperty(name="Skip Sries Select",
default=True) # type: ignore

read_as: bpy.props.EnumProperty(name="Read as",
default="scalar",
Expand All @@ -166,7 +154,7 @@ def execute(self, context):
data_path = Path(self.filepath).resolve()
ext = get_ext(data_path)
if ext not in SUPPORT_EXTS:
self.report({"WARNING"}, "Not supported extension.")
self.report({"WARNING"}, "Not supported format.")
return {'CANCELLED'}

print("Collecting Meta Data...")
Expand Down Expand Up @@ -297,12 +285,19 @@ def modal(self, context, event):
return {'FINISHED'}

def invoke(self, context, event):
if not self.filepath and not self.directory:
if not self.filepath:
return {'CANCELLED'}

data_path = Path(self.filepath).resolve()
ext = get_ext(data_path)

container_objs = get_container_objs_from_selection()
if len(container_objs) > 0:
self.container_obj_name = container_objs[0].name

title = f"Add to **{self.container_obj_name}**" \
if self.container_obj_name != "" else f"Init a Container"

# Series Selection
if ext in DICOM_EXTS:
data_dirpath = data_path.parent
Expand Down Expand Up @@ -358,37 +353,48 @@ def get_meta(key):
series_item.label = value

if len(series_items.keys()) > 1:
self.skip_series_select = False
context.window_manager.invoke_props_dialog(self,
width=400,
title="Which series to import?")
title=title)
return {'RUNNING_MODAL'}
elif len(series_items.keys()) == 1:
self.series_id = list(series_items.keys())[0]
else:
self.report({"ERROR"}, "Get no vaild series.")
return {'CANCELLED'}

self.execute(context)
return {'RUNNING_MODAL'}
if self.skip_read_as:
self.execute(context)
return {'RUNNING_MODAL'}
else:
context.window_manager.invoke_props_dialog(self,
width=400,
title=title)
return {'RUNNING_MODAL'}

def draw(self, context):
layout = self.layout
layout.label(
text='Detect multi-series in DICOM, pick one')
layout.prop(self, "series_id")
if not self.skip_read_as:
layout.label(
text='What kind is the data read as?')
layout.prop(self, "read_as")
if not self.skip_series_select:
layout.label(text='Which series to import?')
layout.prop(self, "series_id")


def get_sequence_sources(self, context):
items = [("-1", "None (Static)", "")]
items = [("-1", "None (1 frame)", "")]
orig_shape = tuple(self.orig_shape)
if self.frame_count > 1:
items.append(("0", f"Frame (Get {self.frame_count} frames)", ""))
items.append(("0", f"Frame ({self.frame_count} frames)", ""))
elif self.frame_count == 1 and self.channel_count > 1:
items.append(("4", f"Channel (Get {self.channel_count} frames)", ""))
items.append(("4", f"Channel ({self.channel_count} frames)", ""))
elif self.frame_count == 1 and self.channel_count == 1:
items.append(("1", f"X (Get {orig_shape[0]} frames)", ""))
items.append(("2", f"Y (Get {orig_shape[1]} frames)", ""))
items.append(("3", f"Z (Get {orig_shape[2]} frames)", ""))
items.append(("1", f"X ({orig_shape[0]} frames)", ""))
items.append(("2", f"Y ({orig_shape[1]} frames)", ""))
items.append(("3", f"Z ({orig_shape[2]} frames)", ""))

return items

Expand Down Expand Up @@ -681,12 +687,9 @@ def modal(self, context, event):
return {'FINISHED'}

def invoke(self, context, event):
if self.read_as == "label":
volume_dtype = "Label"
elif self.read_as == "scalar":
volume_dtype = "Scalar"
title = f"As {volume_dtype} Opitons (Add to Container: {self.container_obj_name})" \
if self.container_obj_name != "" else f"As {volume_dtype} Options (Init a Container)"
layer_kind = self.read_as.capitalize()
title = f"Add to **{self.container_obj_name}**, As {layer_kind}" \
if self.container_obj_name != "" else f"Init a Container, As {layer_kind}"
context.window_manager.invoke_props_dialog(self,
width=500,
title=title)
Expand Down
12 changes: 6 additions & 6 deletions bioxelnodes/operators/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ class SaveStagedData(bpy.types.Operator):
bl_label = "Save Staged Data"
bl_description = "Save all staged data in this file for sharing"

save_layer: bpy.props.BoolProperty(
name="Save Layer VDB Cache",
save_cache: bpy.props.BoolProperty(
name="Save Layer Caches",
default=True,
) # type: ignore

Expand Down Expand Up @@ -112,11 +112,11 @@ def execute(self, context):

self.report({"INFO"}, f"Successfully saved to {output_path}")

if self.save_layer:
if self.save_cache:
fails = []
for layer in get_all_layer_objs():
try:
save_layer(layer, self.cache_dir)
save_layer_cache(layer, self.cache_dir)
except:
fails.append(layer)

Expand All @@ -140,15 +140,15 @@ def poll(cls, context):
def draw(self, context):
layout = self.layout
panel = layout.box()
panel.prop(self, "save_layer")
panel.prop(self, "save_cache")
panel.prop(self, "cache_dir")
panel = layout.box()
panel.prop(self, "save_lib")
panel.prop(self, "lib_dir")


class SaveCaches(bpy.types.Operator):
bl_idname = "bioxelnodes.save_layers"
bl_idname = "bioxelnodes.save_caches"
bl_label = "Save Caches"
bl_description = "Save Container's caches to directory."

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "bioxelnodes"
version = "0.3.1"
version = "0.3.2"
description = ""
authors = ["Ma Nan <icrdr2010@outlook.com>"]
license = "MIT"
Expand Down

0 comments on commit 2429eef

Please sign in to comment.