Skip to content

Commit b601560

Browse files
Ability to use depthmap information
It is now possible to specify the depth at various points of the flatmount image, for more accurate reconstruction. This is achieved via a single-channel TIFF file called depthmap.tiff, and a corresponding CSV file depthmap.csv, which specifies the background value. It is also possible to switch easily between 3D views of flatmount and reconstruction in the GUI.
1 parent 9aac12a commit b601560

40 files changed

+1073
-85
lines changed

doc/retistruct-user-guide.tex

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
\documentclass{book}
22

3-
\newcommand{\svninfo}{Retistruct version: 0.6.2 , Date: 2019-12-13}
3+
\newcommand{\svninfo}{Retistruct version: 0.7.0 , Date: 2020-08-26}
44
\pagestyle{myheadings}
55
\markboth{\svninfo}{\svninfo}
66

@@ -245,7 +245,7 @@ \section{Reading files using the \textsc{ImageJ} ROI format}
245245
\textbf{Outline colour}.
246246
\end{enumerate}
247247

248-
\subsection{Reading in images comprising multiple fragments}
248+
\subsection{Reading in images comprising multiple fragments [Optional]}
249249
\label{retistruct-user-guide:sec:read-imag-compr}
250250

251251
Images from retinae that been cut into multiple fragments can also be
@@ -259,9 +259,38 @@ \subsection{Specifying the scale} Optionally, the scale of the image
259259
lines, the first with the heading \texttt{Scale} and the second with
260260
the length of the side of a pixel in micrometres. For example:
261261
\begin{verbatim}
262-
"Scale"
262+
"XY"
263263
1.5
264264
\end{verbatim}
265+
Note that for compatibility with older versions of
266+
\texttt{Retistruct}, this column can also be called ``Scale''.
267+
268+
\subsection{Specifying depth information [Optional]}
269+
\label{retistruct-user-guide:sec:specifying-depth}
270+
271+
A ``flat-mount'' needn't be flat. If depth information is available,
272+
this should be supplied in the form of a single-channel TIFF file
273+
called \texttt{depthmap.tiff} with the same dimensions as
274+
\texttt{image.png}. The value of each pixel in the TIFF file specifies
275+
the height of the corresponding pixel in the image. The size of each
276+
step in the Z-direction should be specified using an extra column
277+
``Z'' in \texttt{scale.csv}.
278+
279+
Parts of the TIFF image will correspond to parts of the flat-mount that
280+
are empty; we refer to these parts as the background. Typically the
281+
background is represented by a value of 0. There will be problems if
282+
the outline specified in ImageJ strays onto parts of the TIFF depthmap
283+
image, since it might appear that there are very sharp jumps in the
284+
depth. To get around this problem, it is necessary to specify the
285+
value of the background, so that \textsc{Retistruct} can infer the
286+
depth of any background regions that the outline strays onto by
287+
looking at the closest depths that are in the foreground. To specify
288+
the background value, include a file \texttt{depthmap.csv}, in the
289+
following format:
290+
\begin{verbatim}
291+
"Background value"
292+
0
293+
\end{verbatim}
265294

266295
\subsection{Reading in data points}
267296
\label{retistruct-user-guide:sec:read-data-points}
@@ -702,7 +731,7 @@ \subsection{Transform}
702731
\section{Exporting the plots}
703732
\label{retistruct-user-guide:sec:exporting-plots}
704733

705-
The \textbf{Bitmap} and \textbf{PDF} buttons above the flatmount view
734+
The \textbf{Bitmap} and \textbf{PDF} buttons above the flat-mount view
706735
and the reconstructed view export the image show to a PNG file format
707736
or an PDF respectively. To change the width of the image in pixels,
708737
select \textbf{Properties} and set the
@@ -1028,3 +1057,4 @@ \chapter{The IDT Data format}
10281057
% LocalWords: Csefalvay's workspace od ReconstructedOutline getDss
10291058
% LocalWords: ReconstructedDataset getSss Xenial Github datacounts
10301059
% LocalWords: Wulff Az El uncheck lll Azimuthal Conformal RoiSet
1060+
% LocalWords: depthmap

pkg/retistruct/DESCRIPTION

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Description: Reconstructs retinae by morphing a flat surface with cuts (a
1212
Version: 0.7.0
1313
URL: http://davidcsterratt.github.io/retistruct/
1414
BugReports: https://github.com/davidcsterratt/retistruct/issues
15-
Date: 2020-08-23
15+
Date: 2020-08-26
1616
Depends:
1717
R (>= 3.5.0)
1818
Imports:
@@ -25,7 +25,8 @@ Imports:
2525
RTriangle (>= 1.6-0.9),
2626
rgl,
2727
R.matlab,
28-
R6
28+
R6,
29+
tiff
2930
Suggests:
3031
spelling,
3132
testthat,

pkg/retistruct/NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export(kr.yhat.cart)
6060
export(line.line.intersection)
6161
export(list.datasets)
6262
export(lvsLplot)
63+
export(morph.dataset.to.parabola)
6364
export(normalise.angle)
6465
export(orthographic)
6566
export(panlabel)

pkg/retistruct/NEWS

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
CHANGES IN VERSION 0.7.0 - Released 2020/08/23
1+
CHANGES IN VERSION 0.7.0 - Released 2020/08/26
22

33
NEW FEATURES
44

5-
* Ability to stitch together separate petals or "fragments"
5+
* Stitching together separate petals or "fragments"
66

77
* Ability to magnify plots in the GUI
88

9+
* Depthmaps, i.e. the ability to specify the depth at various points
10+
of the flatmount image, for more accurate reconstruction.
11+
12+
* Switch easily between 3D views of flatmount and reconstruction in
13+
GUI
14+
915
CHANGES IN VERSION 0.6.3 - Released 2020/04/03
1016

1117
BUG FIX

pkg/retistruct/R/Outline.R

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ Outline <- R6Class("Outline",
1010
##' @field P A N-by-2 matrix of points of the \code{Outline}
1111
##' arranged in anticlockwise order
1212
P=NULL,
13-
##' @field scale The length of one unit of \code{P} in arbitrary units
14-
scale=NULL,
13+
##' @field scale The length of one unit of \code{P} in the X-Y plane in arbitrary units
14+
scale=NULL,
15+
##' @field scalez The length of one unit of \code{P} in the Z-direction in arbitrary units
16+
scalez=NULL,
1517
##' @field units String giving units of scaled P, e.g. \dQuote{um}
1618
units=NA,
1719
##' @field gf For each row of \code{P}, the index of \code{P} that
@@ -25,19 +27,32 @@ Outline <- R6Class("Outline",
2527
h=NULL,
2628
##' @field im An image as a \code{raster} object
2729
im=NULL,
30+
##' @field dm Depthmap, with same dimensions as \code{im}, which indicates
31+
##' height of each pixel in Z-direction
32+
dm=NULL,
2833
##' @field A.fragments Areas of fragments
2934
A.fragments = NULL,
3035
##' @description Construct an outline object. This sanitises the
3136
##' input points \code{P}.
3237
##' @param fragments A list of N-by-2 matrix of points for each fragment of the \code{Outline}
3338
##' @param scale The length of one unit of \code{P} in arbitrary units
3439
##' @param im The image as a \code{raster} object
40+
##' @param scalez The length of one unit of \code{P} in the Z-direction in arbitrary units. If \code{NA}, the depthmap is ignored.
41+
##' @param dm Depthmap, with same dimensions as \code{im}, which indicates
42+
##' height of each pixel in Z-direction
3543
##' @param units String giving units of scaled P, e.g. \dQuote{um}
36-
initialize=function(fragments=list(), scale=NA, im=NULL, units=NA) {
37-
self$P <- matrix(0, 0, 3)
38-
colnames(self$P) <- c("X", "Y", "FID")
44+
initialize=function(fragments=list(), scale=NA, im=NULL, scalez=NA, dm=NULL, units=NA) {
45+
self$P <- matrix(0, 0, 4)
46+
colnames(self$P) <- c("X", "Y", "Z", "FID")
47+
if (!is.null(dm) & !is.null(im)) {
48+
if (all(dim(im) != dim(dm))) {
49+
stop("Image and depthmap must have the same dimensions")
50+
}
51+
}
3952
self$im <- im
53+
self$dm <- dm
4054
self$scale <- scale
55+
self$scalez <- scalez
4156
self$units <- sub(units, "um", "\U00B5m")
4257
if (!is.list(fragments)) {
4358
fragments <- list(fragments)
@@ -60,6 +75,9 @@ Outline <- R6Class("Outline",
6075
##' @description Image setter
6176
##' @param im An image as a \code{raster} object
6277
replaceImage = function(im) {
78+
if (all(dim(im) != dim(self$dm))) {
79+
stop("Image and depthmap must have the same dimensions")
80+
}
6381
self$im <- im
6482
},
6583
##' @description Map the point IDs of a \link{Fragment} on the
@@ -84,22 +102,42 @@ Outline <- R6Class("Outline",
84102
y[pids[nna]] <- pids[x[nna]]
85103
return(y)
86104
},
105+
##' @description Get depth of points P
106+
##' @param P matrix containing unscaled X-Y coordinates of points
107+
##' @return Vector of unscaled Z coordinates of points P
108+
getDepth = function(P) {
109+
if (!is.null(self$dm)) {
110+
Z <- interpolate.image(self$dm, P, invert.y=TRUE)
111+
} else {
112+
Z <- rep(0, nrow(P))
113+
}
114+
return(Z)
115+
},
116+
87117
##' @description Add points to the outline register of points
88-
##' @param P 2 column matrix of points to add
89-
##' @param fid fragment id of the points
118+
##' @param P 2 or 3 column matrix of points to add
119+
##' @param fid ID of fragment to which to add the points
90120
##' @return The ID of each added point in the register. If points already
91121
##' exist a point will not be created in the register,
92122
##' but an ID will be returned
93123
addPoints = function(P, fid) {
94-
if (!is.matrix(P)) {
95-
if (length(P) == 2) {
124+
if (!(is.vector(P) | is.matrix(P))) {
125+
stop("P must be a matrix with 2 or 3 columns, or a vector of length 2 or 3")
126+
}
127+
if (is.vector(P)) {
128+
if (length(P) == 2 | length(P) == 3) {
96129
P <- matrix(P, nrow=1)
97130
} else {
98-
stop("P must be a matrix or vector of length 2")
131+
stop("P should be vector of length 2 or 3")
132+
}
133+
}
134+
if (is.matrix(P)) {
135+
if (!(ncol(P) == 2 | ncol(P) == 3)) {
136+
stop("P should be a matrix with 2 or 3 columns")
99137
}
100138
}
101-
if (!(ncol(P) == 2)) {
102-
stop("P must have 2 (X, Y) columns")
139+
if (ncol(P) == 2) {
140+
P <- cbind(P, self$getDepth(P))
103141
}
104142
pids <- rep(NA, nrow(P))
105143
for (i in (1:nrow(P))) {
@@ -109,7 +147,7 @@ Outline <- R6Class("Outline",
109147
self$h[1] <- 1
110148
} else {
111149
## Check point doesn't already exist
112-
id <- which(apply(t(self$P[,-3,drop=FALSE]) == P[i,], 2, all))
150+
id <- which(apply(t(self$P[,1:2,drop=FALSE]) == P[i,1:2], 2, all))
113151
if (length(id) > 1) {
114152
stop(paste("Point register has duplicates", self$P[id,], collapse=", "))
115153
}
@@ -139,7 +177,7 @@ Outline <- R6Class("Outline",
139177
##' @param fid fragment id of the points
140178
##' @return Vector of points
141179
getFragmentPoints = function(fid) {
142-
return(self$P[self$getFragmentPointIDs(fid),c("X", "Y")])
180+
return(self$P[self$getFragmentPointIDs(fid),c("X", "Y", "Z")])
143181
},
144182
##' @description Get fragment
145183
##' @param fid Fragment ID
@@ -167,18 +205,36 @@ Outline <- R6Class("Outline",
167205
return(unique(self$P[,"FID"]))
168206
},
169207
##' @description Get unscaled mesh points
208+
##' @param pids IDs of point to return
209+
##' @return Matrix with columns \code{X}, \code{Y} and \code{Z}
210+
getPoints = function(pids=NULL) {
211+
if (!is.null(pids)) {
212+
return(self$P[pids,c("X", "Y", "Z")])
213+
}
214+
return(self$P[,c("X", "Y", "Z")])
215+
},
216+
##' @description Get X-Y coordinates of unscaled mesh points
217+
##' @param pids IDs of point to return
170218
##' @return Matrix with columns \code{X} and \code{Y}
171-
getPoints = function() {
219+
getPointsXY = function(pids=NULL) {
220+
if (!is.null(pids)) {
221+
return(self$P[pids,c("X", "Y")])
222+
}
172223
return(self$P[,c("X", "Y")])
173224
},
174225
##' @description Get scaled mesh points
175226
##' @return Matrix with columns \code{X} and \code{Y} which is
176227
##' exactly \code{scale} times the matrix returned by \code{getPoints}
177228
getPointsScaled = function() {
178229
if (is.na(self$scale)) {
179-
return(self$P[,c("X", "Y")])
230+
return(self$P[,c("X", "Y", "Z")])
231+
}
232+
if (is.na(self$scalez)) {
233+
return(cbind(self$scale*self$P[,c("X", "Y")],
234+
Z=0))
180235
}
181-
return(cbind(self$scale*self$P[,c("X", "Y")]))
236+
return(cbind(self$scale*self$P[,c("X", "Y")],
237+
Z=self$scalez*self$P[,"Z"]))
182238
},
183239
##' @description Get set of points on rim
184240
##' @return Vector of point IDs, i.e. indices of the rows in

pkg/retistruct/R/PathOutline.R

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,19 @@ PathOutline <- R6Class("PathOutline",
5656
if (!((self$gf[i0] == i1) || (self$gf[i1] == i0))) {
5757
stop("Points", i0, "and", i1, "are not connected by an edge")
5858
}
59+
if ((f >= 1) | (f <= 0)) {
60+
stop("f argument should be between 0 and 1")
61+
}
5962
fid <- self$getFragmentIDsFromPointIDs(i0)
6063
fid1 <- self$getFragmentIDsFromPointIDs(i1)
6164
if (fid != fid1) {
6265
stop("Fragment IDs of points differ")
6366
}
64-
PXY <- self$getPoints()
6567
p <-
66-
(1 - f)*PXY[i0,] +
67-
f* PXY[i1,]
68+
(1 - f)*self$getPointsXY(i0) +
69+
f* self$getPointsXY(i1)
6870
## Find the index of any row of P that matches p
69-
n <- anyDuplicated(rbind(PXY, p),
71+
n <- anyDuplicated(rbind(self$getPointsXY(c(i0, i1)), p),
7072
fromLast=TRUE)
7173
if (n == 0) {
7274
## If the point p doesn't exist
@@ -101,6 +103,10 @@ PathOutline <- R6Class("PathOutline",
101103
Sf <- path.length(VF0, VF1, self$gf, self$hf, self$getPointsScaled())
102104
Sb <- path.length(VB0, VB1, self$gb, self$hb, self$getPointsScaled())
103105

106+
report(paste("\nstitchSubpaths(", VF0, ",", VF1, ",", VB0, ",", VB1, ",", epsilon, ")\n"))
107+
108+
report(paste("Sf =", Sf, ", Sb =", Sb, "\n"))
109+
104110
## Initialise forward and backward indicies to first points in each
105111
## path
106112
i0 <- VF0

pkg/retistruct/R/ReconstructedOutline.R

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ ReconstructedOutline <- R6Class("ReconstructedOutline",
113113
## ol$orderRset()
114114
self$ol <- ol
115115
self$phi0 <- ol$phi0
116-
self$lambda0 <- ol$lambda0
116+
self$lambda0 <- ol$lambda0
117117

118118
report("Merging points...")
119119
self$mergePointsEdges()
@@ -645,7 +645,8 @@ ReconstructedOutline <- R6Class("ReconstructedOutline",
645645
as.vector(outer(ys, xs*0, FUN="+")))
646646

647647
## Find Barycentric coordinates of corners of pixels
648-
Ib <- tsearch(self$ol$getPoints()[,"X"], self$ol$getPoints()[,"Y"],
648+
649+
Ib <- tsearch(self$ol$getPointsXY()[,"X"], self$ol$getPointsXY()[,"Y"],
649650
self$ol$T, I[,1], I[,2], bary=TRUE)
650651
rm(I)
651652
gc()
@@ -848,7 +849,7 @@ flatplot.ReconstructedOutline <- function(x, axt="n",
848849
} else {
849850
col <- grid.min.col
850851
}
851-
P1 <- get.gridline.flat(x$ol$getPoints(), x$ol$T, x$phi, x$lambda, x$Tt,
852+
P1 <- get.gridline.flat(x$ol$getPointsXY(), x$ol$T, x$phi, x$lambda, x$Tt,
852853
c(0,0,1), sin(Phi*pi/180))
853854
cols <- c(cols, rep(col, nrow(P1)))
854855
P <- rbind(P, P1)
@@ -860,7 +861,7 @@ flatplot.ReconstructedOutline <- function(x, axt="n",
860861
col <- grid.min.col
861862
}
862863
Lambda <- Lambda * pi/180
863-
P1 <- get.gridline.flat(x$ol$getPoints(), x$ol$T, x$phi, x$lambda, x$Tt,
864+
P1 <- get.gridline.flat(x$ol$getPointsXY(), x$ol$T, x$phi, x$lambda, x$Tt,
864865
c(sin(Lambda),cos(Lambda),0), 0)
865866
cols <- c(cols, rep(col, nrow(P1)))
866867
P <- rbind(P, P1)

pkg/retistruct/R/StitchedOutline.R

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ StitchedOutline <- R6Class("StitchedOutline",
5757
self$h <- r$h
5858
for (i in 1:nrow(self$tears)) {
5959
self$stitchSubpaths(self$tears[i,"V0"], self$tears[i,"VF"],
60-
self$tears[i,"V0"], self$tears[i,"VB"],
61-
epsilon=self$epsilon)
60+
self$tears[i,"V0"], self$tears[i,"VB"],
61+
epsilon=self$epsilon)
6262
}
6363

6464
## Link up points on rim
@@ -85,11 +85,6 @@ StitchedOutline <- R6Class("StitchedOutline",
8585
paste(self$Rset, collapse=", ")))
8686
}
8787

88-
## VF0 <- self$corrs[,1]
89-
## VF1 <- self$corrs[,2]
90-
## VB0 <- self$corrs[,4]
91-
## VB1 <- self$corrs[,3]
92-
9388
for (i in 1:nrow(self$corrs)) {
9489
self$stitchSubpaths(self$corrs[i,"VF0"], self$corrs[i,"VF1"],
9590
self$corrs[i,"VB0"], self$corrs[i,"VB1"],

pkg/retistruct/R/TriangulatedOutline.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ TriangulatedOutline <- R6Class("TriangulatedOutline",
6969
}
7070
## Find areas and lengths of connections
7171
P <- self$getPointsScaled()
72-
self$A <- tri.area(cbind(P, 0), self$T)
72+
self$A <- tri.area(P, self$T)
7373
self$L <- vecnorm(P[self$Cu[,1],] - P[self$Cu[,2],])
7474
self$A.tot <- sum(self$A)
7575
},

0 commit comments

Comments
 (0)