Commit 2ceba27c authored by Sean Fitzgibbon's avatar Sean Fitzgibbon
Browse files

Added support for jpeg2k in slide registration

parent 3d33fb27
......@@ -15,7 +15,7 @@ import numpy as np
import matplotlib.pyplot as plt
def register_chart_to_slide(chart, slide, out, rlevel=3, boundary_key='outline'):
def register_chart_to_slide(chart, slide, out, rlevel=0, boundary_key='outline'):
# load chart
contour, cells = neurolucida.read(chart)
......@@ -53,8 +53,9 @@ def register_chart_to_slide(chart, slide, out, rlevel=3, boundary_key='outline')
# apply opt-xfm to contours and cells and save
contour_xfm = {k: _apply_xfm(
opt, v[:, :2] * [1, -1]).tolist() for k, v in contour.items()}
contour_xfm = {
k: _apply_xfm(opt, v[:, :2] * [1, -1]).tolist() for k, v in contour.items()
}
with open(f'{out}/contour.json', 'w') as fp:
json.dump(contour_xfm, fp, indent=4)
......
......@@ -20,6 +20,7 @@
"file": "",
"storage": "mem",
"dtype": "f4",
"resolution_level": 4,
"mask": {
"file": null,
"normalise": true,
......@@ -37,6 +38,7 @@
"file": "",
"storage": "mem",
"dtype": "f4",
"resolution_level": 4,
"mask": {
"file": null,
"normalise": true,
......
......@@ -132,8 +132,7 @@ from tirl.transformations.nonlinear.displacement import TxDisplacementField
import matplotlib.pyplot as plt
import os.path as op
# TIRL IMPORTS
# DEFINITIONS
from slider import util
# Rotation search: number of best initialisations to test
N_BEST = 3
......@@ -142,8 +141,37 @@ SNAPSHOT_EXT = "png"
# NumPy print formatting
np.set_printoptions(precision=4)
def _load_image(p):
if p.file.lower().endswith('.jp2'): # if jpg2k
# load jp2
import glymur
from tirl.scripts.mnd.image import set_mask
jp2= glymur.Jp2k(p.file)
img = jp2.read(rlevel=p.resolution_level)
# adjust resolution by resolution_level
p.resolution = p.resolution * (2**p.resolution_level)
# create timg
timg = TImage.fromarray(img, dtype=p.dtype, tensor_axes=(2,))
timg.resolution = p.resolution
# add mask
set_mask(timg, scope=globals(), **p.mask)
# Export
tirl.scripts.mnd.inout.export(timg, p.export, default=None)
# Snapshot
tirl.scripts.mnd.inout.snapshot(timg, p.snapshot, default=None)
else: # else other image types
timg = tirl.scripts.mnd.inout.load_image(scope=globals(), **p)
return timg
# IMPLEMENTATION
def run(cnf=None, **options):
"""
......@@ -179,12 +207,16 @@ def run(cnf=None, **options):
ext = ts.EXTENSIONS["TImage"]
p.moving.export = \
os.path.join(p.general.outputdir, f"moving.{ext}")
moving = tirl.scripts.mnd.inout.load_image(scope=globals(), **p.moving)
# TODO: adjust resolution based on rlevel.
moving = _load_image(p.moving)
if p.fixed.export is True:
ext = ts.EXTENSIONS["TImage"]
p.fixed.export = os.path.join(p.general.outputdir, f"fixed.{ext}")
fixed = tirl.scripts.mnd.inout.load_image(scope=globals(), **p.fixed)
fixed = _load_image(p.fixed)
# Having loaded both images, perform actions on the histology image prior
# to registration, unless it was loaded from a TImage.
......@@ -579,6 +611,44 @@ def affine2d(fixed, moving, cnf):
moving.resample(1, copy=False)
def diffreg2d(fixed, moving, cnf):
"""
Performs a non-linear registration. The transformation is parameterised as
a dense displacement field. The cost is MIND, and diffusion regularisation
is used to create smoothness in the deformation field.
"""
p = AttrMap(cnf)
q = p.regparams.nonlinear
logger = logging.getLogger(p.logger)
# Scaling-smoothing iteration
for i, (sc, sm) in enumerate(zip(q.scaling, q.smoothing)):
logger.debug(f"Scale: {sc}, smoothing: {sm} px...")
# Prepare images for the current iteration
fixed.resample(1 / sc, copy=False)
moving.resample(1 / sc, copy=False)
fixed_smooth = fixed.smooth(sm, copy=True)
moving_smooth = moving.smooth(sm, copy=True)
# Prepare transformation to optimise
tx_nonlinear = fixed_smooth.domain.chain[-1]
# Set cost and regulariser
cost = CostMIND(moving_smooth, fixed_smooth, sigma=float(q.sigma),
truncate=float(q.truncate), kernel=MK_FULL)
regularisation = DiffusionRegulariser(
tx_nonlinear, weight=float(q.regweight))
# Optimise the non-linear transformation
GNOptimiserDiffusion(
tx_nonlinear, cost, regularisation, maxiter=int(q.maxiter[i]),
xtol_rel=q.xtol_rel, xtol_abs=q.xtol_abs, visualise=q.visualise,
logger=logger)()
# Transfer optimised transformations to the non-smoothed images
fixed.domain = fixed_smooth.domain
moving.domain = moving_smooth.domain
else:
# Restore the original resolution of the images
fixed.resample(1, copy=False)
moving.resample(1, copy=False)
# AUXILIARY FUNCTIONS
......@@ -798,6 +868,9 @@ def register_slide_to_slide(moving, moving_res, fixed, fixed_res, out, config):
"""
if config is None:
config = util.get_resource('default.json')
if os.path.isfile(config):
with open(config, "r") as fp:
config = dict(json.load(fp))
......
#!/usr/bin/env python
import os.path as op
def get_slider_dir() -> str:
rpath = op.realpath(op.dirname(op.abspath(__file__)))
return rpath
def get_resource_path() -> str:
rpath = op.realpath(op.join(op.dirname(op.abspath(__file__)), "resources"))
return rpath
def get_resource(name) -> str:
r = op.join(get_resource_path(), name)
# asrt.assert_file_exists(r)
return r
......@@ -19,15 +19,17 @@ def add_slide_cli(subparsers):
help="Moving slide image", type=str)
parser.add_argument("moving_res", metavar="<moving-resolution>",
help="Moving image resolution (mm)", type=float)
parser.add_argument("fixed", metavar="<fixed>",
help="Fixed slide image", type=str)
parser.add_argument("fixed_res", metavar="<fixed-resolution>",
help="Fixed image resolution (mm)", type=float)
parser.add_argument("--out", metavar="<dir>",
help="Output directory", default='./slider.reg', type=str,
required=False)
parser.add_argument("--config", metavar="<default.json>",
help="configuration file", default='default.json', type=str,
parser.add_argument("--config", metavar="<config.json>",
help="configuration file", default=None, type=str,
required=False)
parser.set_defaults(method='slide')
......
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