Commit 1924d969 authored by Sean Fitzgibbon's avatar Sean Fitzgibbon
Browse files

no longer requires a boundary key

parent 607a6b4f
......@@ -35,6 +35,7 @@ from skimage import (
from typing import Optional
from slider import util
from scipy.spatial import ConvexHull
def register_chart_to_slide(
......@@ -42,24 +43,23 @@ def register_chart_to_slide(
slide: str,
slide_res: float,
outdir: str,
boundary_key: Optional[str] = ("Outline", "outline", "Section Outline"),
config: Optional[str] = None,
do_plots: Optional[bool] = None,
justify: Optional[str] = None,
field: Optional[str] = None,
It takes a chart and a slide and aligns the chart to the slide.
It takes a chart and a slide, and aligns the chart to the slide.
chart (str): The path to the chart file.
slide (str): The slide image.
slide (str): The slide to be registered.
slide_res (float): The resolution of the slide.
outdir (str): The output directory for the results.
boundary_key (Optional[str]): The name of the boundary contour in the chart.
config (Optional[str]): Path to the YAML configuration file.
config (Optional[str]): Path to the configuration file.
do_plots (Optional[bool]): Whether to plot the results of the alignment.
justify (Optional[str]): str, optional
justify (Optional[str]): str
if config is None:
config = util.get_resource("chart.yaml")
......@@ -69,8 +69,8 @@ def register_chart_to_slide(
rlevel = config["slide"]["resolution_level"]
if boundary_key is None:
boundary_key = config["chart"]["boundary_key"]
# if boundary_key is None:
# boundary_key = config["chart"]["boundary_key"]
if do_plots is None:
do_plots = config["general"]["plots"]
......@@ -83,22 +83,14 @@ def register_chart_to_slide(
chart = util.Chart.from_neurolucida_ascii(chart)
if isinstance(boundary_key, str):
outline = chart.get_contours(name=boundary_key, closed=True)
outline = [chart.get_contours(name=k, closed=True) for k in boundary_key]
outline = [item for sublist in outline for item in sublist]
if len(outline) < 1:
raise ValueError(f"Boundary key {boundary_key} not found in chart")
if len(outline) > 1:
f"Repeated entries of boundary key in chart ({boundary_key}). They will be merged."
outline = chart.get_contours(closed=True)
edge_crds = [x.points[:, :2] * [1, -1] for x in outline]
edge_crds_cat = np.concatenate(edge_crds)
# hull = ConvexHull(edge_crds_cat)
# edge_crds_cat = np.concatenate([edge_crds_cat[simplex,:] for simplex in hull.simplices], axis=0)
# load slide, convert to grayscale, and invert if light-field
slide_res = slide_res * (2 ** rlevel)
......@@ -108,7 +100,16 @@ def register_chart_to_slide(
img = color.rgb2gray(img_orig)
field = estimate_field(img)
# if exclude_zeros:
# nnz_mask = img != 0
# else:
# nnz_mask = None
# r, c = img.shape
if field is None:
field = estimate_field(img)
print(f'Estimated field is: {field}')
if field == "light":
img = 1 - img
......@@ -283,6 +284,7 @@ def register_chart_to_slide(
plot_contour(ax[7], coords, name, color=cmap(idx))
fig.savefig(f"{outdir}/alignment.png", bbox_inches="tight", dpi=300)
# plt.close(fig)
def plot_normals(ax, line_x, line_y, contour_x, contour_y, color="b"):
......@@ -331,13 +333,16 @@ def plot_contour(ax, coords, name=None, color="r", title=None, linewidth=1, **kw
def estimate_field(img):
def estimate_field(img, mask=None):
"""Estimate field of slide as being light or dark"""
# calculate mean intensity of the four corners of the image
corners = [img[:20, :20], img[:20, -20:], img[-20:, :20], img[-20:, -20:]]
if mask is None:
edges = np.zeros(img.shape, dtype=bool)
edges[:, 0:2], edges[:, -2:], edges[0:2, :], edges[-2:, :] = True, True, True, True
edges = mask ^ morphology.binary_erosion(mask, selem=morphology.disk(10))
corners = np.median(np.concatenate(corners, axis=0))
edges_m = np.median(img[edges])
# calculate mean intensity of the centre of the image
h, w = img.shape
......@@ -345,10 +350,12 @@ def estimate_field(img):
w = w // 2
centre = img[h - 100 : h + 100, w - 100 : w + 100]
centre = np.mean(centre)
centre_m = np.median(centre)
# print(edges_m, centre_m)
# estimate field (light or dark)
if corners > centre:
if edges_m > centre_m:
return "light"
return "dark"
......@@ -406,6 +413,22 @@ def segment_foreground(
def optimise_xfm(mask, mask_resolution, contour, xfm, n_iter=100):
Given a mask, a contour, and an initial transformation, optimise_xfm() iteratively optimises the
transformation by finding the best fit between the contour and the mask, and then returns the best
fit transformation.
mask: the binary mask of the structure of interest
mask_resolution: The resolution of the mask image.
contour: the contour to be transformed
xfm: the initial transform
n_iter: number of iterations to run the optimisation for. Defaults to 100
The optimise_xfm function returns the optimised xfm, the mean distance between the optimised xfm
and the contour, and the optimised xfm and the contour at each iteration.
mdist = np.zeros(n_iter)
iter_props = [None] * n_iter
......@@ -570,7 +593,20 @@ def justify_bbox(bbox, aspect_ratio, justify):
def initialise_xfm(image, image_resolution, contour, tol=0.05, justify=None):
"""Calculate image and contour bounding boxes, then estimate the transform (xfm) based on bounding box corners."""
Calculate the transform (xfm) based on bounding box corners
image: the image to be transformed
image_resolution: The resolution of the image.
contour: the contour to be transformed
tol: tolerance for aspect ratio mismatch between slide and contour
justify: str
the transform (xfm), the image properties (image_p) and the contour properties (contour_p).
brainmask = segment_foreground(image)
Supports Markdown
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