Merge pull request #45 from OmooLab/feature/dev

fix: relocate cannot work on missing layer
This commit is contained in:
Ma Nan
2024-09-19 12:28:35 +08:00
committed by GitHub
11 changed files with 68 additions and 49 deletions

View File

@@ -77,7 +77,7 @@ class Layer():
for f in range(self.frame_count): for f in range(self.frame_count):
mask_frame = mask[f, :, :, :] mask_frame = mask[f, :, :, :]
if smooth > 0: if smooth > 0:
mask_frame = scipy.minimum_filter(mask_frame.astype(np.float32), mask_frame = scipy.median_filter(mask_frame.astype(np.float32),
mode="nearest", mode="nearest",
size=smooth) size=smooth)
# mask_frame = scipy.median_filter( # mask_frame = scipy.median_filter(
@@ -87,7 +87,7 @@ class Layer():
for f in range(self.frame_count): for f in range(self.frame_count):
mask_frame = mask[:, :, :] mask_frame = mask[:, :, :]
if smooth > 0: if smooth > 0:
mask_frame = scipy.minimum_filter(mask_frame.astype(np.float32), mask_frame = scipy.median_filter(mask_frame.astype(np.float32),
mode="nearest", mode="nearest",
size=smooth) size=smooth)
# mask_frame = scipy.median_filter( # mask_frame = scipy.median_filter(
@@ -119,10 +119,6 @@ class Layer():
if progress_callback: if progress_callback:
progress_callback(f, self.frame_count) progress_callback(f, self.frame_count)
# frame = ski.resize(data[f, :, :, :],
# shape,
# preserve_range=True,
# anti_aliasing=data.dtype.kind != "b")
frame = data[f, :, :, :, :] frame = data[f, :, :, :, :]
if smooth > 0: if smooth > 0:
frame = scipy.median_filter(frame.astype(np.float32), frame = scipy.median_filter(frame.astype(np.float32),

View File

@@ -259,11 +259,12 @@ def local_lib_not_updated():
not_update = get_node_version() != addon_version not_update = get_node_version() != addon_version
return use_local and not_update return use_local and not_update
def get_output_node(node_group): def get_output_node(node_group):
try: try:
output_node = get_nodes_by_type(node_group, output_node = get_nodes_by_type(node_group,
'NodeGroupOutput')[0] 'NodeGroupOutput')[0]
except: except:
output_node = node_group.nodes.new("NodeGroupOutput") output_node = node_group.nodes.new("NodeGroupOutput")
return output_node return output_node

View File

@@ -1,13 +1,13 @@
from pathlib import Path from pathlib import Path
import bpy import bpy
from .common import get_file_prop, get_node_lib_path, set_file_prop from .common import get_file_prop, set_file_prop
from ..exceptions import Incompatible, NoFound from ..exceptions import NoFound
from ..constants import LATEST_NODE_LIB_PATH, VERSIONS from ..constants import LATEST_NODE_LIB_PATH, VERSIONS
def get_node_group(node_type: str, use_link=True): def get_node_tree(node_type: str, use_link=True):
# unannotate below for local debug in node lib file. # unannotate below for local debug in node lib file.
# node_group = bpy.data.node_groups[node_type] # node_group = bpy.data.node_groups[node_type]
# return node_group # return node_group
@@ -19,36 +19,37 @@ def get_node_group(node_type: str, use_link=True):
if get_file_prop("node_version") is None: if get_file_prop("node_version") is None:
set_file_prop("node_version", addon_version) set_file_prop("node_version", addon_version)
local_lib_path = None local_lib = None
for node_group in bpy.data.node_groups: for node_group in bpy.data.node_groups:
if node_group.name.startswith("BioxelNodes"): if node_group.name.startswith("BioxelNodes"):
lib = node_group.library lib = node_group.library
if lib: if lib:
lib_path = Path(bpy.path.abspath(lib.filepath)).resolve() lib_path = Path(bpy.path.abspath(lib.filepath)).resolve()
if lib_path != addon_lib_path: if lib_path != addon_lib_path:
local_lib_path = lib_path local_lib = lib.filepath
break break
# local lib first # local lib first
lib_path = local_lib_path or addon_lib_path lib_path_str = local_lib or str(addon_lib_path)
bpy.ops.wm.append('EXEC_DEFAULT',
directory=f"{lib_path.as_posix()}/NodeTree",
filename=node_type,
link=use_link,
use_recursive=True,
do_reuse_local_id=True)
node_group = bpy.data.node_groups.get(node_type)
if node_group is None: with bpy.data.libraries.load(lib_path_str,
link=use_link,
relative=True) as (data_from, data_to):
data_to.node_groups = [n for n in data_from.node_groups
if n == node_type]
node_tree = data_to.node_groups[0]
if node_tree is None:
raise NoFound('No custom node found') raise NoFound('No custom node found')
return node_group return node_tree
def assign_node_group(node, node_type: str): def assign_node_tree(node, node_tree):
node.node_tree = bpy.data.node_groups[node_type] node.node_tree = node_tree
node.width = 200.0 node.width = 200.0
node.name = node_type node.name = node_tree.name
return node return node
@@ -61,9 +62,9 @@ def add_node_to_graph(node_name: str, node_group, node_label=None, use_link=True
if node.select: if node.select:
node.select = False node.select = False
get_node_group(node_type, use_link) node_tree = get_node_tree(node_type, use_link)
node = node_group.nodes.new("GeometryNodeGroup") node = node_group.nodes.new("GeometryNodeGroup")
assign_node_group(node, node_type) assign_node_tree(node, node_tree)
node.label = node_label node.label = node_label
node.show_options = False node.show_options = False

View File

@@ -1,7 +1,7 @@
schema_version = "1.0.0" schema_version = "1.0.0"
id = "bioxelnodes" id = "bioxelnodes"
version = "1.0.1" version = "1.0.2"
name = "Bioxel Nodes" name = "Bioxel Nodes"
tagline = "For scientific volumetric data visualization in Blender" tagline = "For scientific volumetric data visualization in Blender"
maintainer = "Ma Nan <icrdr2010@outlook.com>" maintainer = "Ma Nan <icrdr2010@outlook.com>"

View File

@@ -1,6 +1,6 @@
from pathlib import Path from pathlib import Path
VERSIONS = [{"label": "Latest", "node_version": (1, 0, 1)}, VERSIONS = [{"label": "Latest", "node_version": (1, 0, 2)},
{"label": "v0.3.x", "node_version": (0, 3, 3)}, {"label": "v0.3.x", "node_version": (0, 3, 3)},
{"label": "v0.2.x", "node_version": (0, 2, 9)}] {"label": "v0.2.x", "node_version": (0, 2, 9)}]

View File

@@ -343,7 +343,7 @@ class AddCutter():
if len(cut_nodes) == 0: if len(cut_nodes) == 0:
cutter_node = add_node_to_graph("ObjectCutter", cutter_node = add_node_to_graph("ObjectCutter",
node_group, node_group,
node_label=name, node_label=f"{self.cutter_type.capitalize()} Cutter",
use_link=get_use_link()) use_link=get_use_link())
cutter_node.inputs[0].default_value = self.cutter_type.capitalize() cutter_node.inputs[0].default_value = self.cutter_type.capitalize()
cutter_node.inputs[1].default_value = cutter_obj cutter_node.inputs[1].default_value = cutter_obj

View File

@@ -164,7 +164,7 @@ class ParseVolumetricData(bpy.types.Operator):
read_as: bpy.props.EnumProperty(name="Read as", read_as: bpy.props.EnumProperty(name="Read as",
default="SCALAR", default="SCALAR",
items=[("SCALAR", "Scalar", ""), items=[("SCALAR", "Scalar", ""),
("LABEL", "Labels", ""), ("LABEL", "Label", ""),
("COLOR", "Color", "")]) # type: ignore ("COLOR", "Color", "")]) # type: ignore
series_id: bpy.props.EnumProperty(name="Select Series", series_id: bpy.props.EnumProperty(name="Select Series",
@@ -284,6 +284,10 @@ class ParseVolumetricData(bpy.types.Operator):
self.report({"ERROR"}, "Invaild label data.") self.report({"ERROR"}, "Invaild label data.")
return {'CANCELLED'} return {'CANCELLED'}
if self.label_count == 0:
self.report({"ERROR"}, "Get no label.")
return {'CANCELLED'}
orig_shape = self.meta['xyz_shape'] orig_shape = self.meta['xyz_shape']
orig_spacing = self.meta['spacing'] orig_spacing = self.meta['spacing']
@@ -418,8 +422,8 @@ class ParseVolumetricData(bpy.types.Operator):
series_id = "empty" series_id = "empty"
label = "{:<20} {:>1}".format(f"{study_description}>{series_description}({series_modality})", label = "{:<20} {:>1}".format(f"{study_description}>{series_description}({series_modality})",
f"({size_x}x{size_y})x{count}") f"({size_x}x{size_y})x{count}")
series_items[series_id] = label series_items[series_id] = label
for series_id, label in series_items.items(): for series_id, label in series_items.items():
@@ -500,10 +504,13 @@ class ImportVolumetricDataDialog(bpy.types.Operator):
label_count: bpy.props.IntProperty() # type: ignore label_count: bpy.props.IntProperty() # type: ignore
smooth: bpy.props.IntProperty(name="Smooth Size (Larger takes longer time)",
default=3) # type: ignore
read_as: bpy.props.EnumProperty(name="Read as", read_as: bpy.props.EnumProperty(name="Read as",
default="SCALAR", default="SCALAR",
items=[("SCALAR", "Scalar", ""), items=[("SCALAR", "Scalar", ""),
("LABEL", "Labels", ""), ("LABEL", "Label", ""),
("COLOR", "Color", "")]) # type: ignore ("COLOR", "Color", "")]) # type: ignore
bioxel_size: bpy.props.FloatProperty(name="Bioxel Size (Larger size means small resolution)", bioxel_size: bpy.props.FloatProperty(name="Bioxel Size (Larger size means small resolution)",
@@ -616,12 +623,15 @@ class ImportVolumetricDataDialog(bpy.types.Operator):
progress_callback = progress_callback_factory(name_i, progress_callback = progress_callback_factory(name_i,
progress, progress,
progress_step) progress_step)
label_data = data == np.full_like(data, i+1)
label_data = label_data.astype(np.float32)
try: try:
layer = Layer(data=data == np.full_like(data, i+1), layer = Layer(data=label_data,
name=name_i, name=name_i,
kind=kind) kind=kind)
layer.resize(shape=shape, layer.resize(shape=shape,
smooth=self.smooth,
progress_callback=progress_callback) progress_callback=progress_callback)
layer.affine = affine layer.affine = affine
@@ -924,6 +934,8 @@ class ImportVolumetricDataDialog(bpy.types.Operator):
if self.read_as == "SCALAR": if self.read_as == "SCALAR":
panel.prop(self, "split_channel", panel.prop(self, "split_channel",
text=f"Split Channel as Multi Layer") text=f"Split Channel as Multi Layer")
elif self.read_as == "LABEL":
panel.prop(self, "smooth")
panel.label( panel.label(
text="Dimension Order: [Frame, (X-axis,Y-axis,Z-axis), Channel]") text="Dimension Order: [Frame, (X-axis,Y-axis,Z-axis), Channel]")

View File

@@ -4,7 +4,6 @@ import bpy
import numpy as np import numpy as np
from ..exceptions import NoContent from ..exceptions import NoContent
from ..bioxel.layer import Layer from ..bioxel.layer import Layer
from ..bioxelutils.node import add_node_to_graph from ..bioxelutils.node import add_node_to_graph
@@ -199,7 +198,7 @@ class RetimeLayer(bpy.types.Operator, LayerOperator):
return self.execute(context) return self.execute(context)
class RelocateLayer(bpy.types.Operator, LayerOperator): class RelocateLayer(bpy.types.Operator):
bl_idname = "bioxelnodes.relocate_layer" bl_idname = "bioxelnodes.relocate_layer"
bl_label = "Relocate Layer Cache" bl_label = "Relocate Layer Cache"
bl_description = "Relocate layer cache" bl_description = "Relocate layer cache"
@@ -207,6 +206,16 @@ class RelocateLayer(bpy.types.Operator, LayerOperator):
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
def execute(self, context):
layer_obj = get_selected_layer(context)
if layer_obj == None:
self.report({"WARNING"}, "Get no layer.")
return {'FINISHED'}
self.operate(layer_obj, context)
return {'FINISHED'}
def operate(self, layer_obj, context): def operate(self, layer_obj, context):
layer_obj.data.filepath = self.filepath layer_obj.data.filepath = self.filepath
@@ -259,7 +268,7 @@ class ResampleLayer(bpy.types.Operator, OutputLayerOperator):
bl_description = "Resample value" bl_description = "Resample value"
bl_icon = "ALIASED" bl_icon = "ALIASED"
smooth: bpy.props.IntProperty(name="Smooth Iteration", smooth: bpy.props.IntProperty(name="Smooth Size",
default=0, default=0,
soft_min=0, soft_max=5, soft_min=0, soft_max=5,
options={"SKIP_SAVE"}) # type: ignore options={"SKIP_SAVE"}) # type: ignore
@@ -389,7 +398,7 @@ class FillByThreshold(bpy.types.Operator, FillOperator):
if self.invert else data > self.threshold if self.invert else data > self.threshold
new_layer = orig_layer.copy() new_layer = orig_layer.copy()
new_layer.fill(self.fill_value, mask, 3) new_layer.fill(self.fill_value, mask, 0)
new_layer.name = self.new_layer_name \ new_layer.name = self.new_layer_name \
or f"{orig_layer.name}_F-{self.threshold:.2f}" or f"{orig_layer.name}_F-{self.threshold:.2f}"
return new_layer return new_layer
@@ -419,7 +428,7 @@ class FillByRange(bpy.types.Operator, FillOperator):
(data > self.from_min) & (data < self.from_max) (data > self.from_min) & (data < self.from_max)
new_layer = orig_layer.copy() new_layer = orig_layer.copy()
new_layer.fill(self.fill_value, mask, 3) new_layer.fill(self.fill_value, mask, 0)
new_layer.name = self.new_layer_name \ new_layer.name = self.new_layer_name \
or f"{orig_layer.name}_F-{self.from_min:.2f}-{self.from_max:.2f}" or f"{orig_layer.name}_F-{self.from_min:.2f}-{self.from_max:.2f}"
return new_layer return new_layer
@@ -431,7 +440,7 @@ class FillByLabel(bpy.types.Operator, FillOperator):
bl_description = "Fill value by label" bl_description = "Fill value by label"
bl_icon = "MESH_CAPSULE" bl_icon = "MESH_CAPSULE"
smooth: bpy.props.IntProperty(name="Smooth Iteration", smooth: bpy.props.IntProperty(name="Smooth Size",
default=0, default=0,
soft_min=0, soft_max=5, soft_min=0, soft_max=5,
options={"SKIP_SAVE"}) # type: ignore options={"SKIP_SAVE"}) # type: ignore
@@ -451,7 +460,7 @@ class FillByLabel(bpy.types.Operator, FillOperator):
mask = 1 - mask mask = 1 - mask
new_layer = orig_layer.copy() new_layer = orig_layer.copy()
new_layer.fill(self.fill_value, mask, 3) new_layer.fill(self.fill_value, mask, 0)
new_layer.name = self.new_layer_name \ new_layer.name = self.new_layer_name \
or f"{orig_layer.name}_F-{label_layer.name}" or f"{orig_layer.name}_F-{label_layer.name}"
return new_layer return new_layer

View File

@@ -1,7 +1,7 @@
import bpy import bpy
from ..bioxelutils.common import is_incompatible, local_lib_not_updated from ..bioxelutils.common import is_incompatible, local_lib_not_updated
from ..bioxelutils.node import assign_node_group, get_node_group from ..bioxelutils.node import assign_node_tree, get_node_tree
from ..utils import get_use_link from ..utils import get_use_link
@@ -41,14 +41,14 @@ class AddNode(bpy.types.Operator):
"Current addon verison is not compatible to this file. If you insist on editing this file please keep the same addon version.") "Current addon verison is not compatible to this file. If you insist on editing this file please keep the same addon version.")
return {'CANCELLED'} return {'CANCELLED'}
get_node_group(self.node_type, get_use_link()) node_tree = get_node_tree(self.node_type, get_use_link())
bpy.ops.node.add_node( bpy.ops.node.add_node(
'INVOKE_DEFAULT', 'INVOKE_DEFAULT',
type='GeometryNodeGroup', type='GeometryNodeGroup',
use_transform=True use_transform=True
) )
node = bpy.context.active_node node = bpy.context.active_node
assign_node_group(node, self.node_type) assign_node_tree(node, node_tree)
node.label = self.node_label node.label = self.node_label
node.show_options = False node.show_options = False

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "bioxelnodes" name = "bioxelnodes"
version = "1.0.1" version = "1.0.2"
description = "" description = ""
authors = ["Ma Nan <icrdr2010@outlook.com>"] authors = ["Ma Nan <icrdr2010@outlook.com>"]
license = "MIT" license = "MIT"