Skip to content

Commit

Permalink
Add fit_xform_brain with test
Browse files Browse the repository at this point in the history
  • Loading branch information
jefferis committed Oct 7, 2022
1 parent 15ef36b commit 11d7957
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 0 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export(display_slice)
export(download_reg_repo)
export(extra_reg_folders)
export(fit_xform)
export(fit_xform_brain)
export(guess_templatebrain)
export(is.templatebrain)
export(local_reg_dir_for_url)
Expand Down
108 changes: 108 additions & 0 deletions R/fit-xform.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,111 @@ fit_xform <- function(reg, samplepts, refpts=NULL, type = c("affine","rigid", "s
}
xt
}

sample_points_in_surf2 <- function(bb, n, x) {
mm=mapply(runif, min=bb[1,], max=bb[2,], n = n)
colnames(mm)=c("X","Y","Z")
data.frame(mm, inside=pointsinside(mm,x))
}

# n defines the target number of points to find as well as the chunk size
# to use.
sample_points_in_surf <- function(x, n){
max_failures=100
min_chunksize=100
chunksize=pmax(n, min_chunksize)
if(!inherits(x, 'mesh3d'))
x=as.mesh3d(x)
bb=boundingbox(x)
nin=0
l=list()
# keep track of the number of times we fail to find a point to avoid
# infinite loops
nfails=0
while(nin<n && nfails<max_failures) {
df=sample_points_in_surf2(bb, chunksize, x)
nin=nin+sum(df$inside, na.rm = T)
if(any(df$inside))
l[[length(l)+1]]=df[df$inside,c("X","Y","Z")]
else nfails=nfails+1
}
dff=dplyr::bind_rows(l)
dff[seq_len(n),,drop=F]
}



#' Fit a single transform to a bridging registration between brains
#'
#' @param pts Optional set of points to use for the fit. If they are not
#' specified they will be randomly sampled (see details).
#' @param npts Number of points to use for fit (defaults to 300 when no
#' \code{pts} argument is specified).
#' @param ... Additional arguments passed to \code{\link{fit_xform}}
#' @inheritParams xform_brain
#' @inheritParams fit_xform
#'
#' @details If pts are supplied these will be used to construct the fit. This
#' might be useful if you are focussing on a particular brain region and
#' supply relevant neurons. But if you want to work with one registration to
#' use for the whole brain then it's better to use points across the brain.
#'
#' When points are not provided then \code{fit_xform_brain} will first check
#' to see if it can find a neuropil surface model for the \code{sample}. If it
#' can, it will sample a number of points that lie inside the surface model.
#' If no surface model can be found, then it will look for a
#' \code{\link{templatebrain}} object specifying a bounding box around the
#' sample brain. If successful, it will sample \code{npts} within that
#' bounding box. If not there will be an error and you will have to supply the
#' points.
#'
#' @return A homogeneous affine matrix or a \code{\link{tpsreg}} object
#' @seealso \code{\link{fit_xform}}
#' @export
#'
#' @examples
#' \dontrun{
#' library(nat.flybrains)
#' t1=fit_xform_brain(sample='FCWB', reference="JFRC2", type='affine')
#' # run the fit based on a particular group of Kenyon cells
#' t2=fit_xform_brain(sample='FCWB', reference="JFRC2",
#' pts=nat::kcs20, npts=300, type='affine')
#'
#' nclear3d()
#' mfrow3d(1, 2, sharedMouse = TRUE)
#' plot3d(JFRC2)
#' plot3d(xform(FCWB.surf, t1))
#' next3d()
#' plot3d(JFRC2)
#' plot3d(xform(FCWB.surf, t2))
#' # the whole brain surfaces clearly match better when you fit on the whole brain
#' }
fit_xform_brain <- function(sample, reference, via=NULL,
type=c("affine","rigid", "similarity", "tps"),
pts=NULL,
npts=if(is.null(pts)) 300 else NULL,
...) {
sbs=shortest_bridging_seq(sample = sample, reference = reference, via=via)
if(is.null(pts)) {
if(!is.templatebrain(sample))
sample <- get_templatebrain(as.character(sample))
# find surface
atb=all_templatebrains()
m=match(as.character(sample), atb$name)
if(!is.na(m)) {
b = try(get(paste0(atb$name[m], '.surf'), mode = 'list'), silent = T)
if(inherits(b, c('hxsurf', 'mesh3d'))) {
if(interactive())
message("sampling points within surface model for:", as.character(sample))
b=as.mesh3d(b)
}
else {
if(interactive())
message("sampling points within template brain bounding box for:", as.character(sample))
b=as.mesh3d(boundingbox(sample))
}
pts=sample_points_in_surf(b, npts)
} else stop("Failed to find template brain!")
}
fit_xform(reg = sbs, samplepts = pts, type=type, subsample = ifelse(is.null(npts), F, npts), ...)
}
79 changes: 79 additions & 0 deletions man/fit_xform_brain.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions tests/testthat/test-transformation.r
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,14 @@ test_that("can use a bridging registration in regdirs",{
"round trip with cmtk reformatx inversion")
expect_true(mean(nabor::knn(xyzmatrix(kc3), xyzmatrix(kc1), k = 1)$nn.dists)<1.0,
"round trip with pre-inverted registration")

skip_if_not_installed('Morpho')

blreg=matrix(c(1.2134, -0.0165, -0.0081, 0, 0.0155, 1.17, -0.0677, 0, 0.0245,
-0.1086, 0.6865, 0, 127.7245, -20.9117, 5.5061, 1), ncol = 4)
expect_equal(
fit_xform_brain(sample = 'IS2', reference = 'JFRC2', pts = nat::Cell07PNs),
blreg, tolerance = 1e-3)
})

test_that("xform doesn't try to transform when sample==reference", {
Expand Down

0 comments on commit 11d7957

Please sign in to comment.