Commit 47a21f26 authored by ihuszar's avatar ihuszar
Browse files

Slice-to-volume protocol for BigMac dataset.

parent 7a44410b
#!/usr/bin/env python
import tirl
import numpy as np
from tirl.costs.mind import CostMIND
from skimage.segmentation import slic
from skimage.future import graph
from skimage.exposure import rescale_intensity
import matplotlib
matplotlib.use("macosx")
import matplotlib.pyplot as plt
from skimage.measure import regionprops
N_POINTS = 16
MASK_UTHR = 1
MASK_LTHR = 0.1
img = tirl.load("/Users/inhuszar/bigmac/reg/stage2.timg")
vol = tirl.load("/Users/inhuszar/bigmac/reg/vol.timg")
mri = vol.evaluate(img.domain)
mask = np.logical_and(img.data > MASK_LTHR, img.data <= MASK_UTHR)
cost = CostMIND(mri, img, precompute=False, normalise=True)
cm = cost.costmap().reduce_tensors(copy=True)
im = rescale_intensity(cm.data, out_range=np.uint8).astype(np.float64) \
* mask.astype(np.int64)
test_cases = (1,) #2 ** np.arange(2, 8)
shape_measure = []
distance_measure = []
for n_segments in test_cases:
# Segment cost function map given the number of segments
im = rescale_intensity(cm.data, out_range=np.uint8).astype(np.float64) \
* mask.astype(np.int64)
segments = slic(im, n_segments=150, sigma=5, compactness=50) \
* mask.astype(np.int64)
g = graph.rag_mean_color(cm.data, segments)
segments = graph.cut_normalized(segments, g, thresh=0.1, in_place=False)
N = np.unique(segments).size - 1
print(N)
regions = regionprops(segments, cm.data)
regions = sorted(regions, key=lambda r: np.std(r.intensity_image))[::-1]
# Measure the average shape parameter of the segments
convexity = np.mean([r.equivalent_diameter for r in regions])
shape_measure.append((N, convexity))
# Measure the average distance between segment centroids
centroids = [r.centroid for r in regions]
xi = np.stack(centroids, axis=0)
edges = np.max(xi, axis=0) - np.min(xi, axis=0)
edges = edges[np.nonzero(edges)]
epsilon = np.power(np.prod(edges) / len(regions), 1. / edges.size)
distance_measure.append((N, epsilon))
# Create new figure for current segment map
mapfig, mapax = plt.subplots(1, 1)
label = np.zeros_like(segments)
im = mapax.imshow(label, vmin=segments.min(), vmax=segments.max(), zorder=1)
for region in regions:
label[segments == region.label] = region.label
im.set_data(label)
y, x = region.centroid
mapax.plot(x, y, "ro", zorder=2)
mapfig.canvas.draw_idle()
plt.pause(0.25)
else:
pass
plt.pause(1)
else:
# Plot the measured properties versus the number of segments
measfig, measax = plt.subplots(1, 1)
measax.plot(*tuple(zip(*shape_measure)), "b-")
measax.plot(*tuple(zip(*distance_measure)), "r-")
plt.show()
...@@ -5,14 +5,14 @@ ...@@ -5,14 +5,14 @@
"author": "Istvan N Huszar, Amy FD Howard" "author": "Istvan N Huszar, Amy FD Howard"
}, },
"slice": { "slice": {
"file": "/Users/inhuszar/bigmac/histo/H092x/mosaic_colour_mrires.tif", "file": "/Users/inhuszar/bigmac/histo/H092x/mosaic_colour.tif",
"alternative": "/Users/inhuszar/bigmac/reg/stage4.timg", "alternative": null,
"storage": "mem", "storage": "mem",
"mask": { "mask": {
"file": null, "file": null,
"normalise": false "normalise": false
}, },
"resolution": 0.3, "resolution": 0.0045,
"preview": false, "preview": false,
"actions": ["resample_image", "lab_b"] "actions": ["resample_image", "lab_b"]
}, },
...@@ -30,14 +30,13 @@ ...@@ -30,14 +30,13 @@
}, },
"general": { "general": {
"verbosity": "debug", "verbosity": "debug",
"system": "macos", "system": "macosx",
"logfile": "/Users/inhuszar/bigmac/reg/tirl_slice_to_volume.log", "logfile": "/Users/inhuszar/bigmac/reg/tirl_slice_to_volume.log",
"outdir": "/Users/inhuszar/bigmac/reg", "outdir": "/Users/inhuszar/bigmac/reg",
"stages": [5, 2, 3, 4], "stages": [1, 2, 3, 4, 5, 2, 3, 4],
"stage_index": 8, "stage_index": 3,
"snapshot_ext": "png", "snapshot_ext": "png",
"randomseed": 1, "warnings": false
"warnings": false
}, },
"regparams": { "regparams": {
"init": { "init": {
...@@ -56,16 +55,22 @@ ...@@ -56,16 +55,22 @@
"visualise": true, "visualise": true,
"verbose": 4, "verbose": 4,
"slab": { "slab": {
"centre": [0, -4.3, 0], "centre": [0, -4, 0],
"normal": [0, 1, 0], "normal": [0, 1, 0],
"thickness": 1.5, "thickness": 3,
"inits": 5 "inits": 7
}, },
"iterations": 1, "iterations": 2,
"scaling": [8, 4, 4, 2, 2, 1, 1], "scaling": [8, 4, 4, 2, 2, 1, 1],
"smoothing": [0, 1, 0, 1, 0, 1, 0], "smoothing": [0, 1, 0, 1, 0, 1, 0],
"constrained": true, "constrained": true,
"opt_step": 0.1, "opt_step": 0.1,
"stage_1a": {
"xtol_abs": [0.01, 0.01, 0.01, 0.01, 0.01]
},
"stage_1b": {
"xtol_abs": [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]
},
"x0": { "x0": {
"scale2d": [1.0, 1.0], "scale2d": [1.0, 1.0],
"rot2d": -90, "rot2d": -90,
...@@ -77,16 +82,16 @@ ...@@ -77,16 +82,16 @@
"dx0": { "dx0": {
"scale2d_lower_delta": [0.2, 0.2], "scale2d_lower_delta": [0.2, 0.2],
"scale2d_upper_delta": [0.2, 0.2], "scale2d_upper_delta": [0.2, 0.2],
"rot2d_lower_delta": 30.0, "rot2d_lower_delta": 10.0,
"rot2d_upper_delta": 30.0, "rot2d_upper_delta": 10.0,
"trans2d_lower_delta": [10.0, 10.0], "trans2d_lower_delta": [15.0, 15.0],
"trans2d_upper_delta": [10.0, 10.0], "trans2d_upper_delta": [15.0, 15.0],
"warp_lower_dxy": 2.5, "warp_lower_dxy": 2.5,
"warp_lower_dz": 2.5, "warp_lower_dz": 2.5,
"warp_upper_dxy": 2.5, "warp_upper_dxy": 2.5,
"warp_upper_dz": 2.5, "warp_upper_dz": 2.5,
"rot3d_lower_delta": [20.0, 20.0, 20.0], "rot3d_lower_delta": [15.0, 15.0, 15.0],
"rot3d_upper_delta": [20.0, 20.0, 20.0], "rot3d_upper_delta": [15.0, 15.0, 15.0],
"affine3d_lower_delta": [0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5], "affine3d_lower_delta": [0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5],
"affine3d_upper_delta": [0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5], "affine3d_upper_delta": [0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5, 0.1, 0.1, 0.1, 2.5],
"trans3d_lower_delta": [0.3, 0.3, 0.3], "trans3d_lower_delta": [0.3, 0.3, 0.3],
...@@ -116,7 +121,7 @@ ...@@ -116,7 +121,7 @@
"image": true, "image": true,
"snapshot": true "snapshot": true
}, },
"visualise": true, "visualise": false,
"verbose": 4, "verbose": 4,
"scaling": [4, 2, 1, 1], "scaling": [4, 2, 1, 1],
"smoothing": [0, 0, 1, 0], "smoothing": [0, 0, 1, 0],
...@@ -129,33 +134,46 @@ ...@@ -129,33 +134,46 @@
"export": { "export": {
"image": true, "image": true,
"snapshot": true, "snapshot": true,
"halton": true "cpoints": true,
"mask": true
},
"mask": {
"lthr": 0.1,
"uthr": 1
}, },
"n_points": 16, "adaptive": {
"visualise": true, "max_points": 32
},
"visualise": false,
"verbose": 4, "verbose": 4,
"smoothing": [2, 1, 0], "smoothing": [2, 1, 0],
"lower_dz": 3, "lower_dz": 1.5,
"upper_dz": 3, "upper_dz": 1.5,
"reg_weight": 0, "reg_weight": 0,
"opt_step": 0.2, "opt_step": 0.4,
"xtol_abs": 0.1, "xtol_abs": 0.1
"randomseed": 1
}, },
"stage_4": { "stage_4": {
"export": { "export": {
"image": true, "image": true,
"snapshot": true, "snapshot": true,
"halton": true "cpoints": true,
"mask": true
},
"mask": {
"lthr": 0.1,
"uthr": 1
},
"adaptive": {
"max_points": 32
}, },
"n_points": 16,
"visualise": false, "visualise": false,
"verbose": 4, "verbose": 4,
"smoothing": [2], "smoothing": [2, 1, 0],
"lower_dxy": 3, "lower_dxy": 3,
"lower_dz": 3, "lower_dz": 1,
"upper_dxy": 3, "upper_dxy": 3,
"upper_dz": 3, "upper_dz": 1,
"reg_weight": 0, "reg_weight": 0,
"opt_step": 0.2, "opt_step": 0.2,
"xtol_abs": 0.1 "xtol_abs": 0.1
...@@ -164,13 +182,13 @@ ...@@ -164,13 +182,13 @@
"export": { "export": {
"image": true, "image": true,
"snapshot": true, "snapshot": true,
"mask": true "mask": true
}, },
"visualise": false, "visualise": false,
"mask": { "mask": {
"lthr": 0.1, "lthr": 0.1,
"uthr": 1 "uthr": 1
} }
} }
} }
} }
This diff is collapsed.
...@@ -578,7 +578,7 @@ class Interpolator(TIRLObject): ...@@ -578,7 +578,7 @@ class Interpolator(TIRLObject):
# Resurrect the specific type of interpolator instance # Resurrect the specific type of interpolator instance
obj = cls(source=values, n_threads=threads, verbose=verbose, **kwargs) obj = cls(source=values, n_threads=threads, verbose=verbose, **kwargs)
obj.tensor_axes = taxes # obj.tensor_axes = taxes
return obj return obj
def _dump(self): def _dump(self):
......
...@@ -6,6 +6,9 @@ from scipy.interpolate import Rbf ...@@ -6,6 +6,9 @@ from scipy.interpolate import Rbf
import tirl.settings as ts import tirl.settings as ts
from tirl.interpolators.interpolator import Interpolator from tirl.interpolators.interpolator import Interpolator
MODELS = ("multiquadric", "inverse", "gaussian", "linear", "cubic", "quintic",
"thin_plate")
class RbfInterpolator(Interpolator): class RbfInterpolator(Interpolator):
""" """
...@@ -81,10 +84,6 @@ class RbfInterpolator(Interpolator): ...@@ -81,10 +84,6 @@ class RbfInterpolator(Interpolator):
domain = dump["kwargs"].get("domain", None) domain = dump["kwargs"].get("domain", None)
if domain is not None: if domain is not None:
obj.kwargs.update({"domain": domain}) obj.kwargs.update({"domain": domain})
# if domain is not None:
# from pydoc import locate
# dc = locate(domain["type"])
# obj.kwargs.update({"domain": dc._load(domain)})
return obj return obj
def _dump(self): def _dump(self):
...@@ -106,3 +105,16 @@ class RbfInterpolator(Interpolator): ...@@ -106,3 +105,16 @@ class RbfInterpolator(Interpolator):
coordinates = np.asarray(coordinates).reshape(1, -1) coordinates = np.asarray(coordinates).reshape(1, -1)
ipol = Rbf(*self.basis.T, input_array.ravel(), **kwargs) ipol = Rbf(*self.basis.T, input_array.ravel(), **kwargs)
return ipol(*coordinates.T) return ipol(*coordinates.T)
@property
def model(self):
return self.kwargs.get("model")
@model.setter
def model(self, m):
m = str(m).lower()
if m in MODELS:
self.kwargs.update({"model": m})
else:
raise ValueError(f"Unrecognised interpolation model: {m}. Choose "
f"from the following: {MODELS}")
...@@ -96,12 +96,14 @@ class SpatialOperator(Operator): ...@@ -96,12 +96,14 @@ class SpatialOperator(Operator):
taxes = tuple(range(result.ndim - tf.vdim)) taxes = tuple(range(result.ndim - tf.vdim))
result_vshape = result.shape[len(taxes):] result_vshape = result.shape[len(taxes):]
# Preserve domain # Preserve domain with original transformations for non-trimming OP
od = tf.domain.copy()
trimming_operation = result_vshape != tf.vshape trimming_operation = result_vshape != tf.vshape
if not trimming_operation: if not trimming_operation:
new_domain = od.copy() new_domain = tf.domain
# Copy domain for trimming OP
else: else:
od = tf.domain.copy()
margins = np.subtract(tf.vshape, result_vshape) // 2 margins = np.subtract(tf.vshape, result_vshape) // 2
assert all(m == self.radius for m in margins) assert all(m == self.radius for m in margins)
offset = [TxTranslation(*margins)] + od.offset offset = [TxTranslation(*margins)] + od.offset
...@@ -109,7 +111,7 @@ class SpatialOperator(Operator): ...@@ -109,7 +111,7 @@ class SpatialOperator(Operator):
od.kwargs = {k: v for k, v in od.kwargs.items() od.kwargs = {k: v for k, v in od.kwargs.items()
if k not in od.RESERVED_KWARGS} if k not in od.RESERVED_KWARGS}
new_domain = od.__class__.__new__( new_domain = od.__class__.__new__(
od.__class__, result_vshape, *od.transformations, offset=offset, od.__class__, result_vshape, od.transformations, offset=offset,
name=new_name, storage=od.storage, dtype=od.dtype, name=new_name, storage=od.storage, dtype=od.dtype,
instance_mem_limit=od.instance_mem_limit, n_threads=od.threads, instance_mem_limit=od.instance_mem_limit, n_threads=od.threads,
**od.kwargs) **od.kwargs)
...@@ -242,11 +244,14 @@ class SpatialOperator(Operator): ...@@ -242,11 +244,14 @@ class SpatialOperator(Operator):
# Create new TField instance for output # Create new TField instance for output
else: else:
# Preserve domain # Preserve domain with transformations for non-trimming OP
od = tf.domain.copy()
if not trimming_operation: if not trimming_operation:
new_domain = od.copy() new_domain = tf.domain
# Copy domain for trimming OP
else: else:
od = tf.domain.copy()
offset_tx = TxTranslation(*((self.radius,) * self.input.vdim)) offset_tx = TxTranslation(*((self.radius,) * self.input.vdim))
offset = [offset_tx] + od.offset offset = [offset_tx] + od.offset
new_name = "%s_%s" % (od.name, self.name) new_name = "%s_%s" % (od.name, self.name)
......
...@@ -54,7 +54,7 @@ class TensorOperator(Operator): ...@@ -54,7 +54,7 @@ class TensorOperator(Operator):
def _no_chunker(self, tf, op, out, *opargs, **opkwargs): def _no_chunker(self, tf, op, out, *opargs, **opkwargs):
""" """
Performs operation with less overhead. Suitable for small in-memory Performs operation with less overhead. Suitable for small in-memory
arrays only. arrays only. A new instance is returned with the original Domain.
""" """
orig_order = tf.order orig_order = tf.order
...@@ -65,7 +65,7 @@ class TensorOperator(Operator): ...@@ -65,7 +65,7 @@ class TensorOperator(Operator):
result = result.reshape(*tf.vshape, *result.shape[1:]) result = result.reshape(*tf.vshape, *result.shape[1:])
name = "{}_{}".format(tf.name, self.name) name = "{}_{}".format(tf.name, self.name)
result = tf.__class__.fromarray( result = tf.__class__.fromarray(
arr=result, tensor_axes=taxes, copy=False, domain=tf.domain.copy(), arr=result, tensor_axes=taxes, copy=False, domain=tf.domain,
order=None, dtype=None, interpolator=tf.interpolator.__class__, order=None, dtype=None, interpolator=tf.interpolator.__class__,
name=name, storage=tf.storage, **tf.kwargs.copy()) name=name, storage=tf.storage, **tf.kwargs.copy())
result.order = orig_order result.order = orig_order
...@@ -179,7 +179,7 @@ class TensorOperator(Operator): ...@@ -179,7 +179,7 @@ class TensorOperator(Operator):
else: else:
name = "{}_{}".format(tf.name, self.name) name = "{}_{}".format(tf.name, self.name)
out = tf.__class__.__new__( out = tf.__class__.__new__(
tf.__class__, extent=tf.domain.copy(), tf.__class__, extent=tf.domain,
tensor_shape=output_tensor_shape, order=input_orig_order, tensor_shape=output_tensor_shape, order=input_orig_order,
dtype=output_dtype, buffer=None, offset=0, dtype=output_dtype, buffer=None, offset=0,
interpolator=tf.interpolator.__class__, name=name, interpolator=tf.interpolator.__class__, name=name,
......
...@@ -1053,7 +1053,7 @@ class TField(TIRLObject, np.lib.mixins.NDArrayOperatorsMixin): ...@@ -1053,7 +1053,7 @@ class TField(TIRLObject, np.lib.mixins.NDArrayOperatorsMixin):
# Create interpolator instance from class specification # Create interpolator instance from class specification
if issubclass(type(ip), type) and issubclass(ip, Interpolator): if issubclass(type(ip), type) and issubclass(ip, Interpolator):
ip = ip(self.data, n_threads=-1) ip = ip(self, n_threads=-1)
ip._tensor_axes = self.taxes # direct access for speed ip._tensor_axes = self.taxes # direct access for speed
ip._threads = reduce(mul, self.tshape) # direct access for speed ip._threads = reduce(mul, self.tshape) # direct access for speed
......
...@@ -1964,7 +1964,7 @@ class TImage(TField): ...@@ -1964,7 +1964,7 @@ class TImage(TField):
""" """
self.domain.reset() self.domain.reset()
def smooth(self, radius, *radii, spatial_op=None, copy=False): def smooth(self, radius, *radii, spatial_op=None, copy=True):
""" """
Smooths the image uniformly for every tensor component. Anisotropic Smooths the image uniformly for every tensor component. Anisotropic
smoothing can be applied by specifying smoothing radii for each spatial smoothing can be applied by specifying smoothing radii for each spatial
...@@ -1994,9 +1994,12 @@ class TImage(TField): ...@@ -1994,9 +1994,12 @@ class TImage(TField):
as leading arguments. as leading arguments.
:type spatial_op: SpatialOperator :type spatial_op: SpatialOperator
:param copy: :param copy:
If True, the smoothing result is returned as a copy of the original If True (default), the smoothing result is returned as a copy of
TImage. If False, the values will be changed in place. the original TImage (the Domain is preserved). If False, the values
:return: will be changed in place.
:returns: smoothed image
:rtype: TImage
""" """
# Verify smoothing radii # Verify smoothing radii
...@@ -2233,8 +2236,11 @@ class TImage(TField): ...@@ -2233,8 +2236,11 @@ class TImage(TField):
return tuple(tx.parameters) return tuple(tx.parameters)
@resolution.setter @resolution.setter
def resolution(self, *resolution): def resolution(self, resolution):
self.set_resolution(*resolution) if hasattr(resolution, "__iter__"):
self.set_resolution(*resolution)
else:
self.set_resolution(resolution)
def preview(self, centre=None, normal=None, resolution=1, **kwargs): def preview(self, centre=None, normal=None, resolution=1, **kwargs):
""" """
......
...@@ -12,8 +12,19 @@ class TxEmbed(Transformation): ...@@ -12,8 +12,19 @@ class TxEmbed(Transformation):
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INITIALISER ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INITIALISER ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
def __init__(self, source_dim, target_dim, homogeneous=True, **kwargs): def __init__(self, source_dim, target_dim, homogeneous=True, **kwargs):
""" Initialisation of TxEmbed. """ """
Initialisation of TxEmbed.
:param source_dim: number of input dimensions
:type source_dim: int
:param target_dim: number of output dimensions
:type target_dim: int
:param homogeneous: use homogeneous coordinates
:type homogeneous: bool
:param kwargs: additional keyword arguments for TxEmbed
:type kwargs: Any
"""
# Set dimensions # Set dimensions
if not tu.isdimension(source_dim): if not tu.isdimension(source_dim):
raise ValueError("The number of input dimensions must be a " raise ValueError("The number of input dimensions must be a "
......
...@@ -630,23 +630,13 @@ class TxRbfDisplacementField(TxNonLinear): ...@@ -630,23 +630,13 @@ class TxRbfDisplacementField(TxNonLinear):
order = tensor.get("order") order = tensor.get("order")
ipol = tensor.get("interpolator") ipol = tensor.get("interpolator")
fkwargs = tensor.get("kwargs") fkwargs = tensor.get("kwargs")
# TODO: Emergency bugfix: use ipol class, because of 'taxes' mismatch
field = TField(domain, tensor_shape=tshape, buffer=params, field = TField(domain, tensor_shape=tshape, buffer=params,
order=order, interpolator=ipol, **fkwargs) order=order, interpolator=ipol.__class__, **fkwargs)
# Resurrect the TxDisplacementField instance # Resurrect the TxDisplacementField instance
obj = TxRbfDisplacementField(field, axes=axes, **meta) obj = TxRbfDisplacementField(field, axes=axes, **meta)
return obj