Skip to content

Commit

Permalink
feat: more time sequence choice
Browse files Browse the repository at this point in the history
fix: contrast formulation cause leak of high value
fix: empty series_id cause fail to read
chore: restructure folder
chore!: change layer vars
BREAKING CHANGE: not support 4.1 any more
  • Loading branch information
icrdr committed Aug 1, 2024
1 parent 488b05f commit ce397b3
Show file tree
Hide file tree
Showing 57 changed files with 3,062 additions and 4,595 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,13 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

upload_blender_addon:
name: Upload Blender Add-on
needs: draft_release
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
lfs: "true"
- name: Zip Add-on
run: |
zip -r package.zip bioxelnodes
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.draft_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./package.zip
asset_name: BioxelNodes_Addon_${{ needs.draft_release.outputs.version }}.zip
asset_content_type: application/zip

upload_blender_extension:
name: Upload Blender Extension
build_blender_add-on:
name: Build Blender Extension
needs: draft_release
runs-on: ubuntu-latest
strategy:
matrix:
platform: ["windows-x64", "linux-x64", "macos-arm64", "macos-x64"]
steps:
- name: Checkout Code
uses: actions/checkout@v4
Expand All @@ -86,21 +66,10 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Zip Extension
- name: Build Add-on
run: |
pip download SimpleITK==2.3.1 --dest wheels --only-binary=:all: --python-version=3.11 --platform=win_amd64
pip download pyometiff==1.0.0 --dest wheels --only-binary=:all: --python-version=3.11 --platform=win_amd64
pip download mrcfile==1.5.1 --dest wheels --only-binary=:all: --python-version=3.11 --platform=win_amd64
mkdir bioxelnodes/wheels
cp wheels/SimpleITK-2.3.1-cp311-cp311-win_amd64.whl bioxelnodes/wheels/SimpleITK-2.3.1-cp311-cp311-win_amd64.whl
cp wheels/lxml-5.2.2-cp311-cp311-win_amd64.whl bioxelnodes/wheels/lxml-5.2.2-cp311-cp311-win_amd64.whl
cp wheels/tifffile-2024.7.21-py3-none-any.whl bioxelnodes/wheels/tifffile-2024.7.21-py3-none-any.whl
cp wheels/pyometiff-1.0.0-py3-none-any.whl bioxelnodes/wheels/pyometiff-1.0.0-py3-none-any.whl
cp wheels/mrcfile-1.5.1-py2.py3-none-any.whl bioxelnodes/wheels/mrcfile-1.5.1-py2.py3-none-any.whl
rm -r bioxelnodes/externalpackage
cp extension/__init__.py bioxelnodes/__init__.py
cp extension/preferences.py bioxelnodes/preferences.py
cp extension/blender_manifest.toml bioxelnodes/blender_manifest.toml
pip install tomlkit
python build.py ${{ matrix.platform }}
zip -r package.zip bioxelnodes
- name: Upload Release Asset
id: upload-release-asset
Expand All @@ -110,5 +79,5 @@ jobs:
with:
upload_url: ${{ needs.draft_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: ./package.zip
asset_name: BioxelNodes_Extension_${{ needs.draft_release.outputs.version }}.zip
asset_name: BioxelNodes_${{ needs.draft_release.outputs.version }}_${{ matrix.platform }}.zip
asset_content_type: application/zip
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,4 @@ blendcache_*

.secrets

!bioxelnodes/scipy/_nd_image.cp311-win_amd64.pyd
!scipy_ndimage/*/**
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Bioxel Nodes is a Blender addon for scientific volumetric data visualization. It

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

- Fantastic rendering result, also support EEVEE NEXT.
- Realistic rendering result, also support EEVEE NEXT.
- Support multiple formats.
- Support 4D volumetric data.
- All kinds of cutters.
Expand Down Expand Up @@ -61,13 +61,13 @@ Welcome to our [discord server](https://discord.gg/pYkNyq2TjE), if you have any

## Compatible to Newer Version

**Updating this addon may break old files, so read the following carefully before updating**
**v0.3.x is not compatible to v0.2.x, Updating this addon may break old files. Read the following carefully before upgradation**

Before updating this addon, you need to ask yourself whether this project file will be modified again or not, if it's an archived project file, I would recommend that you run **Bioxel Nodes > Save Staged Data** to make the addon nodes permanent. In this way, there will be no potential problem with the nodes not functioning due to the addon update.
Before upgradation, you need to ask yourself whether this project file will be modified again or not, if it's an archived project file, I would recommend that you run **Bioxel Nodes > Save Staged Data** to make the addon nodes permanent. In this way, there will be no potential problem with the nodes not functioning due to the addon update.

After the addon update, your old project files may not work either, this may be because you had executed **Save Staged Data**. If so, you need to execute **Bioxel Nodes > Relink Nodes to Addon** to relink them to make sure that the addon's new functionality and the addon nodes are synchronized.

Also, unlike the newer versions, the older shaders are not based on OSL, so if you find that you can't render volumes, you need to turn on **Open Shading Language (OSL)** in the Render Settings.
Also, the older shaders are not based on OSL, so if you find that you can't render volumes, you need to turn on **Open Shading Language (OSL)** in the Render Settings.

## Roadmap

Expand Down
11 changes: 0 additions & 11 deletions bioxelnodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@
from . import menus


bl_info = {
"name": "Bioxel Nodes",
"author": "Ma Nan",
"description": "",
"blender": (4, 1, 0),
"version": (0, 2, 9),
"location": "File -> Import",
"warning": "",
"category": "Node"
}

auto_load.init()


Expand Down
3 changes: 0 additions & 3 deletions bioxelnodes/assets/Nodes/BioxelNodes_4.1.blend

This file was deleted.

3 changes: 3 additions & 0 deletions bioxelnodes/assets/Nodes/BioxelNodes_4.2.blend
Git LFS file not shown
Empty file added bioxelnodes/bioxel/__init__.py
Empty file.
9 changes: 9 additions & 0 deletions bioxelnodes/bioxel/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .layer import Layer


class Container():
def __init__(self,
name,
layers: list[Layer] = []) -> None:
self.name = name
self.layers = layers
43 changes: 43 additions & 0 deletions bioxelnodes/bioxel/io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from pathlib import Path
import uuid


# 3rd-party
import h5py

from .container import Container
from .layer import Layer


def load_container(load_file: str):
load_path = Path(load_file).resolve()
with h5py.File(load_path, 'r') as file:
layers = []
for key, layer_dset in file['layers'].items():
layers.append(Layer(data=layer_dset[:],
name=layer_dset.attrs['name'],
kind=layer_dset.attrs['kind'],
affine=layer_dset.attrs['affine']))

container = Container(name=file.attrs['name'],
layers=layers)

return container


def save_container(container: Container, save_file: str, overwrite=False):
save_path = Path(save_file).resolve()
if overwrite:
if save_path.is_file():
save_path.unlink()

with h5py.File(save_path, "w") as file:
file.attrs['name'] = container.name
layer_group = file.create_group("layers")
for layer in container.layers:
layer_key = uuid.uuid4().hex[:8]
layer_dset = layer_group.create_dataset(name=layer_key,
data=layer.data)
layer_dset.attrs['name'] = layer.name
layer_dset.attrs['kind'] = layer.kind
layer_dset.attrs['affine'] = layer.affine
131 changes: 131 additions & 0 deletions bioxelnodes/bioxel/layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import copy
import numpy as np

from . import scipy
from . import skimage as ski

# 3rd-party
import transforms3d

# TODO: turn to dataclasses


class Layer():
def __init__(self,
data: np.ndarray,
name: str,
kind="scalar",
affine=np.identity(4)) -> None:
if data.ndim != 5:
raise Exception("Data shape order should be TXYZC")

affine = np.array(affine)
if affine.shape != (4, 4):
raise Exception("affine shape should be (4,4)")

self.data = data
self.name = name
self.kind = kind
self.affine = affine

@property
def bioxel_size(self):
t, r, z, s = transforms3d.affines.decompose44(self.affine)
return z.tolist()

@property
def shape(self):
return self.data.shape[1:4]

@property
def dtype(self):
return self.data.dtype

@property
def origin(self):
t, r, z, s = transforms3d.affines.decompose44(self.affine)
return t.tolist()

@property
def euler(self):
t, r, z, s = transforms3d.affines.decompose44(self.affine)
return list(transforms3d.euler.mat2euler(r))

@property
def min(self):
return float(np.min(self.data))

@property
def frame_count(self):
return self.data.shape[0]

@property
def channel_count(self):
return self.data.shape[-1]

@property
def max(self):
return float(np.max(self.data))

def copy(self):
return copy.deepcopy(self)

def fill(self, value: float, mask: np.ndarray):
mask_frames = ()
if mask.ndim == 4:
if mask.shape[0] != self.frame_count:
raise Exception("Mask frame count is not same as ")
for f in range(self.frame_count):
mask_frame = mask[f, :, :, :]
mask_frame = scipy.median_filter(
mask_frame.astype(np.float32), size=2)
mask_frames += (mask_frame,)
elif mask.ndim == 3:
for f in range(self.frame_count):
mask_frame = mask[:, :, :]
mask_frame = scipy.median_filter(
mask_frame.astype(np.float32), size=2)
mask_frames += (mask_frame,)
else:
raise Exception("Mask shape order should be TXYZ or XYZ")

_mask = np.stack(mask_frames)
_mask = np.expand_dims(_mask, axis=-1)
self.data = _mask * value + (1-_mask) * self.data

def resize(self, shape:tuple, progress_callback=None):
if len(shape) != 3:
raise Exception("Shape must be 3 dim")

data = self.data

# 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_frames = ()
for f in range(self.frame_count):
if progress_callback:
progress_callback(f, self.frame_count)

frame = ski.resize(data[f, :, :, :],
shape,
preserve_range=True,
anti_aliasing=data.dtype.kind != "b")

data_frames += (frame,)

data = np.stack(data_frames)

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

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

self.data = data
Loading

0 comments on commit ce397b3

Please sign in to comment.