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 @@
"author": "Istvan N Huszar, Amy FD Howard"
},
"slice": {
"file": "/Users/inhuszar/bigmac/histo/H092x/mosaic_colour_mrires.tif",
"alternative": "/Users/inhuszar/bigmac/reg/stage4.timg",
"file": "/Users/inhuszar/bigmac/histo/H092x/mosaic_colour.tif",
"alternative": null,
"storage": "mem",
"mask": {
"file": null,
"normalise": false
},
"resolution": 0.3,
"resolution": 0.0045,
"preview": false,
"actions": ["resample_image", "lab_b"]
},
......@@ -30,14 +30,13 @@
},
"general": {
"verbosity": "debug",
"system": "macos",
"system": "macosx",
"logfile": "/Users/inhuszar/bigmac/reg/tirl_slice_to_volume.log",
"outdir": "/Users/inhuszar/bigmac/reg",
"stages": [5, 2, 3, 4],
"stage_index": 8,
"stages": [1, 2, 3, 4, 5, 2, 3, 4],
"stage_index": 3,
"snapshot_ext": "png",
"randomseed": 1,
"warnings": false
"warnings": false
},
"regparams": {
"init": {
......@@ -56,16 +55,22 @@
"visualise": true,
"verbose": 4,
"slab": {
"centre": [0, -4.3, 0],
"centre": [0, -4, 0],
"normal": [0, 1, 0],
"thickness": 1.5,
"inits": 5
"thickness": 3,
"inits": 7
},
"iterations": 1,
"iterations": 2,
"scaling": [8, 4, 4, 2, 2, 1, 1],
"smoothing": [0, 1, 0, 1, 0, 1, 0],
"constrained": true,
"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": {
"scale2d": [1.0, 1.0],
"rot2d": -90,
......@@ -77,16 +82,16 @@
"dx0": {
"scale2d_lower_delta": [0.2, 0.2],
"scale2d_upper_delta": [0.2, 0.2],
"rot2d_lower_delta": 30.0,
"rot2d_upper_delta": 30.0,
"trans2d_lower_delta": [10.0, 10.0],
"trans2d_upper_delta": [10.0, 10.0],
"rot2d_lower_delta": 10.0,
"rot2d_upper_delta": 10.0,
"trans2d_lower_delta": [15.0, 15.0],
"trans2d_upper_delta": [15.0, 15.0],
"warp_lower_dxy": 2.5,
"warp_lower_dz": 2.5,
"warp_upper_dxy": 2.5,
"warp_upper_dz": 2.5,
"rot3d_lower_delta": [20.0, 20.0, 20.0],
"rot3d_upper_delta": [20.0, 20.0, 20.0],
"rot3d_lower_delta": [15.0, 15.0, 15.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_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],
......@@ -116,7 +121,7 @@
"image": true,
"snapshot": true
},
"visualise": true,
"visualise": false,
"verbose": 4,
"scaling": [4, 2, 1, 1],
"smoothing": [0, 0, 1, 0],
......@@ -129,33 +134,46 @@
"export": {
"image": true,
"snapshot": true,
"halton": true
"cpoints": true,
"mask": true
},
"mask": {
"lthr": 0.1,
"uthr": 1
},
"n_points": 16,
"visualise": true,
"adaptive": {
"max_points": 32
},
"visualise": false,
"verbose": 4,
"smoothing": [2, 1, 0],
"lower_dz": 3,
"upper_dz": 3,
"lower_dz": 1.5,
"upper_dz": 1.5,
"reg_weight": 0,
"opt_step": 0.2,
"xtol_abs": 0.1,
"randomseed": 1
"opt_step": 0.4,
"xtol_abs": 0.1
},
"stage_4": {
"export": {
"image": true,
"snapshot": true,
"halton": true
"cpoints": true,
"mask": true
},
"mask": {
"lthr": 0.1,
"uthr": 1
},
"adaptive": {
"max_points": 32
},
"n_points": 16,
"visualise": false,
"verbose": 4,
"smoothing": [2],
"smoothing": [2, 1, 0],
"lower_dxy": 3,
"lower_dz": 3,
"lower_dz": 1,
"upper_dxy": 3,
"upper_dz": 3,
"upper_dz": 1,
"reg_weight": 0,
"opt_step": 0.2,
"xtol_abs": 0.1
......@@ -164,13 +182,13 @@
"export": {
"image": true,
"snapshot": true,
"mask": true
"mask": true
},
"visualise": false,
"mask": {
"lthr": 0.1,
"uthr": 1
}
"mask": {
"lthr": 0.1,
"uthr": 1
}
}
}
}
This diff is collapsed.
......@@ -578,7 +578,7 @@ class Interpolator(TIRLObject):
# Resurrect the specific type of interpolator instance
obj = cls(source=values, n_threads=threads, verbose=verbose, **kwargs)
obj.tensor_axes = taxes
# obj.tensor_axes = taxes
return obj
def _dump(self):
......
......@@ -6,6 +6,9 @@ from scipy.interpolate import Rbf
import tirl.settings as ts
from tirl.interpolators.interpolator import Interpolator
MODELS = ("multiquadric", "inverse", "gaussian", "linear", "cubic", "quintic",
"thin_plate")
class RbfInterpolator(Interpolator):
"""
......@@ -81,10 +84,6 @@ class RbfInterpolator(Interpolator):
domain = dump["kwargs"].get("domain", None)
if domain is not None:
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
def _dump(self):
......@@ -106,3 +105,16 @@ class RbfInterpolator(Interpolator):
coordinates = np.asarray(coordinates).reshape(1, -1)
ipol = Rbf(*self.basis.T, input_array.ravel(), **kwargs)
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):
taxes = tuple(range(result.ndim - tf.vdim))
result_vshape = result.shape[len(taxes):]
# Preserve domain
od = tf.domain.copy()
# Preserve domain with original transformations for non-trimming OP
trimming_operation = result_vshape != tf.vshape
if not trimming_operation:
new_domain = od.copy()
new_domain = tf.domain
# Copy domain for trimming OP
else:
od = tf.domain.copy()
margins = np.subtract(tf.vshape, result_vshape) // 2
assert all(m == self.radius for m in margins)
offset = [TxTranslation(*margins)] + od.offset
......@@ -109,7 +111,7 @@ class SpatialOperator(Operator):
od.kwargs = {k: v for k, v in od.kwargs.items()
if k not in od.RESERVED_KWARGS}
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,
instance_mem_limit=od.instance_mem_limit, n_threads=od.threads,
**od.kwargs)
......@@ -242,11 +244,14 @@ class SpatialOperator(Operator):
# Create new TField instance for output
else:
# Preserve domain
od = tf.domain.copy()
# Preserve domain with transformations for non-trimming OP
if not trimming_operation:
new_domain = od.copy()
new_domain = tf.domain
# Copy domain for trimming OP
else:
od = tf.domain.copy()
offset_tx = TxTranslation(*((self.radius,) * self.input.vdim))
offset = [offset_tx] + od.offset
new_name = "%s_%s" % (od.name, self.name)
......
......@@ -54,7 +54,7 @@ class TensorOperator(Operator):
def _no_chunker(self, tf, op, out, *opargs, **opkwargs):
"""
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
......@@ -65,7 +65,7 @@ class TensorOperator(Operator):
result = result.reshape(*tf.vshape, *result.shape[1:])
name = "{}_{}".format(tf.name, self.name)
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__,
name=name, storage=tf.storage, **tf.kwargs.copy())
result.order = orig_order
......@@ -179,7 +179,7 @@ class TensorOperator(Operator):
else:
name = "{}_{}".format(tf.name, self.name)
out = tf.__class__.__new__(
tf.__class__, extent=tf.domain.copy(),
tf.__class__, extent=tf.domain,
tensor_shape=output_tensor_shape, order=input_orig_order,
dtype=output_dtype, buffer=None, offset=0,
interpolator=tf.interpolator.__class__, name=name,
......
......@@ -1053,7 +1053,7 @@ class TField(TIRLObject, np.lib.mixins.NDArrayOperatorsMixin):
# Create interpolator instance from class specification
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._threads = reduce(mul, self.tshape) # direct access for speed
......
......@@ -1964,7 +1964,7 @@ class TImage(TField):
"""
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
smoothing can be applied by specifying smoothing radii for each spatial
......@@ -1994,9 +1994,12 @@ class TImage(TField):
as leading arguments.
:type spatial_op: SpatialOperator
:param copy:
If True, the smoothing result is returned as a copy of the original
TImage. If False, the values will be changed in place.
:return:
If True (default), the smoothing result is returned as a copy of
the original TImage (the Domain is preserved). If False, the values
will be changed in place.
:returns: smoothed image
:rtype: TImage
"""
# Verify smoothing radii
......@@ -2233,8 +2236,11 @@ class TImage(TField):
return tuple(tx.parameters)
@resolution.setter
def resolution(self, *resolution):
self.set_resolution(*resolution)
def resolution(self, resolution):
if hasattr(resolution, "__iter__"):
self.set_resolution(*resolution)
else:
self.set_resolution(resolution)
def preview(self, centre=None, normal=None, resolution=1, **kwargs):
"""
......
......@@ -12,8 +12,19 @@ class TxEmbed(Transformation):
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INITIALISER ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
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
if not tu.isdimension(source_dim):
raise ValueError("The number of input dimensions must be a "
......
......@@ -630,23 +630,13 @@ class TxRbfDisplacementField(TxNonLinear):
order = tensor.get("order")
ipol = tensor.get("interpolator")
fkwargs = tensor.get("kwargs")
# TODO: Emergency bugfix: use ipol class, because of 'taxes' mismatch
field = TField(domain, tensor_shape=tshape, buffer=params,
order=order, interpolator=ipol, **fkwargs)
order=order, interpolator=ipol.__class__, **fkwargs)
# Resurrect the TxDisplacementField instance
obj = TxRbfDisplacementField(field, axes=axes, **meta)
return obj
# def dump(self):
# field = self.field()
# meta = self.metaparameters.copy()
# meta.pop("tensor")
# meta.pop("domain", None)
# dtx = {"type": ".".join([type(self).__module__,
# type(self).__name__]),
# "field": field.dump(),
# "metaparameters": meta}
# return dtx
def regrid(self, domain):
"""
Returns a transformation object with identical metaparameters. The
......
......@@ -391,21 +391,6 @@ class Transformation(TIRLObject):
"parameters": self.parameters,
"metaparameters": rcopy(self.metaparameters)
})
# meta = dict()
# for key, item in self.metaparameters.items():
# if isinstance(item, TIRLObject):
# if hasattr(item, "dump"):
# meta[key] = getattr(item, "dump")()
# else:
# raise AttributeError(
# "No 'dump' method was found for TIRLObject {}."
# .format(item.__class__.__name__))
# else:
# meta[key] = item
# objdump.update({
# "parameters": self.parameters,
# "metaparameters": meta
# })
return objdump
@classmethod
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment