Skip to content

Commit

Permalink
feat: layer list system
Browse files Browse the repository at this point in the history
  • Loading branch information
icrdr committed Aug 14, 2024
1 parent 21e0b85 commit 9e88586
Show file tree
Hide file tree
Showing 21 changed files with 1,392 additions and 766 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,6 @@ blendcache_*

.secrets

.vdb

!scipy_ndimage/*/**
9 changes: 8 additions & 1 deletion bioxelnodes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import bpy

from .props import BIOXELNODES_LayerListUL
from . import auto_load
from . import menus

Expand All @@ -9,11 +10,17 @@

def register():
auto_load.register()
bpy.types.WindowManager.bioxelnodes_progress_factor = bpy.props.FloatProperty()
bpy.types.WindowManager.bioxelnodes_progress_factor = bpy.props.FloatProperty(
default=1.0)
bpy.types.WindowManager.bioxelnodes_progress_text = bpy.props.StringProperty()
bpy.types.WindowManager.bioxelnodes_layer_list_UL = bpy.props.PointerProperty(
type=BIOXELNODES_LayerListUL)
menus.add()


def unregister():
menus.remove()
del bpy.types.WindowManager.bioxelnodes_progress_factor
del bpy.types.WindowManager.bioxelnodes_progress_text
del bpy.types.WindowManager.bioxelnodes_layer_list_UL
auto_load.unregister()
4 changes: 2 additions & 2 deletions bioxelnodes/assets/Nodes/BioxelNodes_4.2.blend
Git LFS file not shown
22 changes: 13 additions & 9 deletions bioxelnodes/bioxel/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

# 3rd-party
import transforms3d

# TODO: turn to dataclasses


Expand Down Expand Up @@ -105,13 +104,13 @@ def resize(self, shape: tuple, progress_callback=None):
data = self.data
order = 0 if self.dtype == bool else 1

# TXYZC > TXYZ
if self.kind in ['label', 'scalar']:
data = np.amax(data, -1)
# # TXYZC > TXYZ
# if self.kind in ['label', 'scalar']:
# data = np.amax(data, -1)

# if self.kind in ['scalar']:
# dtype = data.dtype
# data = data.astype(np.float32)
# data = data.astype(np.float32)

data_frames = ()
for f in range(self.frame_count):
Expand All @@ -125,8 +124,10 @@ def resize(self, shape: tuple, progress_callback=None):

factors = np.divide(self.shape, shape)
zoom_factors = [1 / f for f in factors]
frame = ndi.zoom(data[f, :, :, :],
zoom_factors,
frame = ndi.zoom(data[f, :, :, :, :],
zoom_factors+[1.0],
mode="nearest",
grid_mode=False,
order=order)

data_frames += (frame,)
Expand All @@ -137,7 +138,10 @@ def resize(self, shape: tuple, progress_callback=None):
# data = data.astype(dtype)

# TXYZ > TXYZC
if self.kind in ['label', 'scalar']:
data = np.expand_dims(data, axis=-1) # expend channel
# if self.kind in ['label', 'scalar']:
# data = np.expand_dims(data, axis=-1) # expend channel

self.data = data

mat_scale = transforms3d.zooms.zfdir2aff(factors[0])
self.affine = np.dot(self.affine, mat_scale)
84 changes: 63 additions & 21 deletions bioxelnodes/bioxel/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
SEQUENCE_EXTS = ['.bmp', '.BMP',
'.jpg', '.JPG', '.jpeg', '.JPEG',
'.tif', '.TIF', '.tiff', '.TIFF',
'.png', '.PNG']
'.png', '.PNG',
'.mrc']


def get_ext(filepath: Path) -> str:
Expand All @@ -64,18 +65,40 @@ def get_ext(filepath: Path) -> str:
return filepath.suffix


def get_file_name(filepath: Path):
def get_filename(filepath: Path):
ext = get_ext(filepath)
return filepath.name.removesuffix(ext).replace(" ", "-")
return filepath.name.removesuffix(ext)


def get_file_number(filepath: Path) -> str:
name = get_file_name(filepath)
def get_filename_parts(filepath: Path) -> str:
def has_digits(s):
return any(char.isdigit() for char in s)

name = get_filename(filepath)
parts = name.replace(".", " ").replace("_", " ").split(" ")
skip_prefixs = ["CH", "ch", "channel"]
number_part = None
number_part_i = None

for i, part in enumerate(parts[::-1]):
if has_digits(part):
if not any([part.startswith(prefix) for prefix in skip_prefixs]):
number_part = part
number_part_i = len(parts)-i
break

if number_part is None:
return name, "", ""

prefix_parts = parts[:number_part_i-1]
prefix_parts_count = sum([len(part)+1 for part in prefix_parts])

digits = ""
suffix = ""

# Iterate through the characters in reverse order
started = False
for char in name[::-1]:
for char in number_part[::-1]:
if char.isdigit():
started = True
# If the character is a digit, add it to the digits string
Expand All @@ -84,29 +107,42 @@ def get_file_number(filepath: Path) -> str:
if started:
# If a non-digit character is encountered, stop the loop
break
else:
suffix += char

digits = digits[::-1]

prefix_parts_count += len(number_part) - \
len(digits) - len(suffix)

# Reverse the digits string to get the correct order
return digits[::-1]
prefix = name[:prefix_parts_count]
suffix = name[prefix_parts_count+len(digits):]

return prefix, digits, suffix

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

def get_file_no_digits_name(filepath: Path) -> str:
prefix, digits, suffix = get_filename_parts(filepath)
prefix = remove_end_str(prefix, "_")
prefix = remove_end_str(prefix, ".")
prefix = remove_end_str(prefix, "-")
prefix = remove_end_str(prefix, " ")
return prefix + suffix

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

def get_file_index(filepath: Path) -> int:
prefix, digits, suffix = get_filename_parts(filepath)
return int(digits) if digits != "" else 0


def collect_sequence(filepath: Path):
file_dict = {}
for f in filepath.parent.iterdir():
if f.is_file() \
and get_ext(filepath) == get_ext(f) \
and get_sequence_name(filepath) == get_sequence_name(f):
index = get_sequence_index(f)
and get_file_no_digits_name(filepath) == get_file_no_digits_name(f):
index = get_file_index(f)
file_dict[index] = f

# reomve isolated seq file
Expand All @@ -124,6 +160,12 @@ def collect_sequence(filepath: Path):
return sequence


def remove_end_str(string: str, end: str):
while string.endswith(end) and len(string) > 0:
string = string.removesuffix(end)
return string


def parse_volumetric_data(data_file: str, series_id="", progress_callback=None) -> Layer:
"""Parse any volumetric data to numpy with shap (T,X,Y,Z,C)
Expand All @@ -139,7 +181,7 @@ def parse_volumetric_data(data_file: str, series_id="", progress_callback=None)
ext = get_ext(data_path)

if progress_callback:
progress_callback(0, "Reading the Data...")
progress_callback(0.0, "Reading the Data...")

is_sequence = False
if ext in SEQUENCE_EXTS:
Expand Down Expand Up @@ -181,7 +223,7 @@ def parse_volumetric_data(data_file: str, series_id="", progress_callback=None)
elif mrc.is_volume_stack():
data = np.expand_dims(data, axis=-1) # expend channel

name = get_file_name(data_path)
name = get_file_no_digits_name(data_path)
spacing = (mrc.voxel_size.x,
mrc.voxel_size.y,
mrc.voxel_size.z)
Expand Down Expand Up @@ -245,7 +287,7 @@ def parse_volumetric_data(data_file: str, series_id="", progress_callback=None)
except:
...

name = get_file_name(data_path)
name = get_file_no_digits_name(data_path)
except:
...

Expand Down Expand Up @@ -297,10 +339,10 @@ def get_meta(key):

elif ext in SEQUENCE_EXTS and is_sequence:
itk_image = sitk.ReadImage(sequence)
name = get_sequence_name(data_path)
name = get_file_no_digits_name(data_path)
else:
itk_image = sitk.ReadImage(data_path)
name = get_file_name(data_path)
name = get_filename(data_path)

# for key in itk_image.GetMetaDataKeys():
# print(f"{key},{itk_image.GetMetaData(key)}")
Expand Down
97 changes: 34 additions & 63 deletions bioxelnodes/bioxelutils/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
from mathutils import Matrix, Vector


from .layer import Layer, get_container_layer_objs, layer_to_obj, obj_to_layer
from .layer import Layer, layer_to_obj, obj_to_layer
from ..nodes import custom_nodes
from .node import get_nodes_by_type, move_node_to_node
from .utils import (get_container_layer_objs,
get_layer_prop_value,
get_nodes_by_type,
move_node_to_node)


NODE_TYPE = {
Expand Down Expand Up @@ -66,59 +69,39 @@ def calc_bbox_verts(origin: tuple, size: tuple):
return bbox_verts


def get_container_objs_from_selection():
container_objs = []
for obj in bpy.context.selected_objects:
if get_container_obj(obj):
container_objs.append(obj)

return list(set(container_objs))


def get_container_obj(current_obj):
if current_obj:
if current_obj.get('bioxel_container'):
return current_obj
elif current_obj.get('bioxel_layer'):
parent = current_obj.parent
return parent if parent.get('bioxel_container') else None
return None
def obj_to_container(container_obj: bpy.types.Object):
layer_objs = get_container_layer_objs(container_obj)
layers = [obj_to_layer(obj) for obj in layer_objs]
container = Container(name=container_obj.name,
layers=layers)
return container


def add_layers(layers: list[Layer],
container_obj: bpy.types.Object,
cache_dir: str):

container_node_group = container_obj.modifiers[0].node_group
node_group = container_obj.modifiers[0].node_group
output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0]

for i, layer in enumerate(layers):
layer_obj = layer_to_obj(layer, container_obj, cache_dir)
mask_node = custom_nodes.add_node(container_node_group,
NODE_TYPE[layer.kind])
mask_node.label = layer_obj.name
mask_node.inputs[0].default_value = layer_obj

# Connect to output if no output linked
output_node = get_nodes_by_type(container_node_group,
'NodeGroupOutput')[0]
fetch_node = custom_nodes.add_node(node_group,
"BioxelNodes_FetchLayer")
fetch_node.label = get_layer_prop_value(layer_obj, "name")
fetch_node.inputs[0].default_value = layer_obj

if len(output_node.inputs[0].links) == 0:
container_node_group.links.new(mask_node.outputs[0],
output_node.inputs[0])
move_node_to_node(mask_node, output_node, (-300, 0))
node_group.links.new(fetch_node.outputs[0],
output_node.inputs[0])
move_node_to_node(fetch_node, output_node, (-600, 0))
else:
move_node_to_node(mask_node, output_node, (0, -100 * (i+1)))
move_node_to_node(fetch_node, output_node, (0, -100 * (i+1)))

return container_obj


def obj_to_container(container_obj: bpy.types.Object):
layer_objs = get_container_layer_objs(container_obj)
layers = [obj_to_layer(obj) for obj in layer_objs]
container = Container(name=container_obj.name,
layers=layers)
return container


def container_to_obj(container: Container,
scene_scale: float,
cache_dir: str):
Expand Down Expand Up @@ -153,28 +136,16 @@ def container_to_obj(container: Container,
container_obj.show_in_front = True
container_obj['bioxel_container'] = True

bpy.ops.node.new_geometry_nodes_modifier()
container_node_group = container_obj.modifiers[0].node_group
input_node = get_nodes_by_type(container_node_group,
'NodeGroupInput')[0]
container_node_group.links.remove(
input_node.outputs[0].links[0])

for i, layer in enumerate(container.layers):
layer_obj = layer_to_obj(layer, container_obj, cache_dir)
mask_node = custom_nodes.add_node(container_node_group,
NODE_TYPE[layer.kind])
mask_node.label = layer_obj.name
mask_node.inputs[0].default_value = layer_obj

# Connect to output if no output linked
output_node = get_nodes_by_type(container_node_group,
'NodeGroupOutput')[0]
if len(output_node.inputs[0].links) == 0:
container_node_group.links.new(mask_node.outputs[0],
output_node.inputs[0])
move_node_to_node(mask_node, output_node, (-300, 0))
else:
move_node_to_node(mask_node, output_node, (0, -100 * (i+1)))
modifier = container_obj.modifiers.new("GeometryNodes", 'NODES')
node_group = bpy.data.node_groups.new('GeometryNodes', 'GeometryNodeTree')
node_group.interface.new_socket(name="Component",
in_out="OUTPUT",
socket_type="NodeSocketGeometry")
modifier.node_group = node_group
node_group.nodes.new("NodeGroupOutput")

container_obj = add_layers(container.layers,
container_obj=container_obj,
cache_dir=cache_dir)

return container_obj
Loading

0 comments on commit 9e88586

Please sign in to comment.