Title: | Stereo Camera Calibration and Reconstruction |
---|---|
Description: | Functions for the collection of 3D points and curves using a stereo camera setup. |
Authors: | Aaron Olsen, Annat Haber |
Maintainer: | Aaron Olsen <[email protected]> |
License: | CC BY-SA 4.0 |
Version: | 1.6.7 |
Built: | 2025-03-12 03:59:26 UTC |
Source: | https://github.com/cran/StereoMorph |
StereoMorph provides functions for the collection of 3D points and curves using a stereo camera setup. StereoMorph can also be used for collecting 2D shape data from photographs. Please see StereoMorph tutorials for step-by-step tutorials on how to use StereoMorph for 2D or 3D shape data collection.
Package: | StereoMorph |
Type: | Package |
Version: | 1.6.7 |
Date: | 2022-05-24 |
License: | CC BY-SA 4.0 |
Aaron Olsen, Annat Haber
Maintainer: Aaron Olsen [email protected]
This function aligns a set of bilateral landmarks to the midline plane. Midline landmarks and the mean position of bilateral landmarks are both used to define the midline plane.
alignLandmarksToMidline(lm.matrix, left = '(_l[_]?|_left[_]?)([0-9]*$)', right = '(_r[_]?|_right[_]?)([0-9]*$)', left.remove = '\\2', right.remove = '\\2', use = rep(TRUE, dim(lm.matrix)[1]), average = FALSE) ## S3 method for class 'alignLandmarksToMidline' summary(object, ...)
alignLandmarksToMidline(lm.matrix, left = '(_l[_]?|_left[_]?)([0-9]*$)', right = '(_r[_]?|_right[_]?)([0-9]*$)', left.remove = '\\2', right.remove = '\\2', use = rep(TRUE, dim(lm.matrix)[1]), average = FALSE) ## S3 method for class 'alignLandmarksToMidline' summary(object, ...)
lm.matrix |
a 2D or 3D matrix with landmark names as row names. |
left |
a regular expression to identify left landmarks in the row names of |
right |
a regular expression to identify right landmarks in the row names of |
left.remove |
an expression for input to the |
right.remove |
an expression for input to the |
use |
a vector of |
average |
Whether to average left and right landmarks after alignment. Can be left false if landmarks previously reflected with |
object |
a list of class |
... |
further arguments passed to or from other methods. |
Currently, the function only accepts left/right designations by matching a regular expression to the landmark name. This is preferable in that it allows for easier match up between bilateral landmarks (based on their common name without a side annotation). The default regular expression identifies left landmarks by a name ending in "_L", "_l", "_left" or "_LEFT", optionally followed by numbers. For example, "hamulus_left", "hamulus_L" and "zymgomatic_arch_l012" would all be identified as landmarks on the left side. Similarly, "hamulus_right", "hamulus_R" and "zymgomatic_arch_r012" would all be identified as landmarks on the right side. Landmarks not identified as left or right are assumed to fall on the midline.
In order to find corresponding left and right landmarks, the function requires the left.remove
and right.remove
arguments. The left.remove
and right.remove
arguments are passed to the base function gsub()
as the replacement
argument. This is used to generate a landmark name that is not side-specific. For example, "hamulus_left" and "zymgomatic_arch_l012" would become "hamulus" and "zymgomatic_arch012". These will be reverted to their original names at return.
Once corresponding right and left landmarks have been identified, the function finds the mean positions of all bilateral landmarks and the positions of all midline landmarks. These points are used to define the midline. After alignment, the specimen will have the midline axis as the last column (z in 3D, y in 2D), the longest non-midline axis as the first column (x in 3D), and the second non-midline axis as y for 3D. No further rotation and reflection is done, therefore the specimen may be facing any direction along each of the axes.
This function returns the aligned landmarks and an error vector, midline.error
. This is a vector of the squared z-coordinate of the midline landmarks (the distance between each midline landmark and the midline plane). If reflectMissingLandmarks
was called on the landmarks prior to alignLandmarksToMidline()
with average
equal to TRUE
, then all of the midline points will fall exactly along the midline. Thus, the error vector will consist entirely of zeros (or near-zero values).
a list of class "alignLandmarksToMidline"
with the following elements:
lm.matrix |
a 2D or 3D matrix of landmarks aligned to the midline. |
midline.error |
a vector of the errors (distances) between each midline landmark and the midline plane. |
This function was modified by A Olsen from the R function AMP()
written by A Haber.
Annat Haber, Aaron Olsen
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET LANDMARKS file <- paste0(fdir, "lm_3d_unify.txt") ## LOAD FILES INTO A MATRIX lm.matrix <- readLandmarksToMatrix(file=file, row.names=1) ## ALIGN TO MIDLINE align_landmarks <- alignLandmarksToMidline(lm.matrix=lm.matrix) ## PRINT SUMMARY OF ERRORS print(summary(align_landmarks))
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET LANDMARKS file <- paste0(fdir, "lm_3d_unify.txt") ## LOAD FILES INTO A MATRIX lm.matrix <- readLandmarksToMatrix(file=file, row.names=1) ## ALIGN TO MIDLINE align_landmarks <- alignLandmarksToMidline(lm.matrix=lm.matrix) ## PRINT SUMMARY OF ERRORS print(summary(align_landmarks))
This function returns the angle (in radians) between two vectors. The vectors can be of any dimension.
avectors(u, v)
avectors(u, v)
u |
a vector |
v |
a vector |
the angle (in radians) between the two input vectors.
Aaron Olsen
## THE ANGLE BETWEEN TWO 2D, ORTHOGONAL VECTORS ## VALUE IS EQUAL TO asin(1/sqrt(2)) u <- c(0, 1) v <- c(1, 0) avectors(u, v) ## THE ANGLE BETWEEN TWO 3D VECTORS ## VALUE IS EQUAL TO asin(sqrt(2)/sqrt(3)) u <- c(1, 1, 1) v <- c(0, 1, 0) avectors(u, v)
## THE ANGLE BETWEEN TWO 2D, ORTHOGONAL VECTORS ## VALUE IS EQUAL TO asin(1/sqrt(2)) u <- c(0, 1) v <- c(1, 0) avectors(u, v) ## THE ANGLE BETWEEN TWO 3D VECTORS ## VALUE IS EQUAL TO asin(sqrt(2)/sqrt(3)) u <- c(1, 1, 1) v <- c(0, 1, 0) avectors(u, v)
This function uses a checkerboard at different positions and angles within a stereo camera setup to estimate DLT calibration coefficients for use in stereo reconstruction. This function is a wrapper for the function dltCalibrateCameras
.
calibrateCameras(img.dir, sq.size, nx, ny, cal.file, corner.dir, print.progress = TRUE, flip.view = FALSE, verify.dir = NULL, error.dir = NULL, min.views = 'max', exec.dir = NULL, undistort = FALSE, num.aspects.read = 'auto', num.sample.est = 'auto', num.sample.sets = 'auto', num.aspects.sample = 'auto', max.sample.optim = 30, nlm.calls.max = 20, fit.min.break = 1, objective.min = 1, objective.min.break = 5, with.circles = FALSE, sample.est = NULL, ...)
calibrateCameras(img.dir, sq.size, nx, ny, cal.file, corner.dir, print.progress = TRUE, flip.view = FALSE, verify.dir = NULL, error.dir = NULL, min.views = 'max', exec.dir = NULL, undistort = FALSE, num.aspects.read = 'auto', num.sample.est = 'auto', num.sample.sets = 'auto', num.aspects.sample = 'auto', max.sample.optim = 30, nlm.calls.max = 20, fit.min.break = 1, objective.min = 1, objective.min.break = 5, with.circles = FALSE, sample.est = NULL, ...)
img.dir |
folder containing images of the checkerboard pattern, separated into separate folders by view. |
sq.size |
character string indicating size of checkerboard squares (length along one dimension) including the unit of measure (e.g. '6.35 mm'). |
nx |
integer indicating the number of internal corners along one dimension of the checkerboard. |
ny |
integer indicating the number of internal corners along the other dimension of the checkerboard. |
cal.file |
file path to the output calibration file (if it does not already exist one will be created). |
corner.dir |
folder where the detected checkerboard corners will be saved (if it does not already exist one will be created). |
print.progress |
logical indicating whether function processes should be printed to the console. |
flip.view |
logical indicating whether one camera view is upside-down relative to the other. |
verify.dir |
folder where the images of the checkerboards with detected corners overlaid will be written (if it does not already exist one will be created). |
error.dir |
folder where the error diagnostic plots will be saved (if it does not already exist one will be created). |
min.views |
integer indicating the minimum views in which corners must be detected in order to use in coefficient estimation. If set to 'max' (default) this will be equal to the number of input views. |
exec.dir |
file path to folder containing external executables for reading video files (still under development). |
undistort |
logical indicating whether to estimate lens distortion correction coefficients (still under development). |
num.aspects.read |
number (integer) of frames to be read from video input. Requires external executables (still under development). |
num.sample.est |
number (integer) of aspects from total that will be sampled calibration coefficient estimation. |
num.sample.sets |
number (integer) of unique sets of aspects to try. |
num.aspects.sample |
number (integer) of aspects to sample for each set. |
max.sample.optim |
maximum number (integer) of aspects to be used in identifying the best DLT coefficient set (once already estimated). |
nlm.calls.max |
maximum number (integer) of different sets of random starting parameters to use during coefficient optimization. This parameter cannot exceed 576. |
fit.min.break |
minimum error at which |
objective.min.break |
minimum error at which optimization will stop estimating the position of additional checkerboards. |
objective.min |
The expected mean reconstruction error when optimizing the calibration coefficients (the minimum, or |
with.circles |
logical indicating whether the checkerboard pattern includes concentric circles for identifying the starting corner. Requires external executables (still under development). |
sample.est |
vector of explicitly defined aspects to use in coefficient estimation. |
... |
further arguments to be passed to |
Please see StereoMorph tutorials for step-by-step tutorials on how to use StereoMorph for 2D or 3D shape data collection.
a list of class "calibrateCameras"
with the following elements:
cal.coeff |
a matrix of 11 optimized DLT calibration coefficients per camera view. |
mean.reconstruct.rmse |
the RMS error when |
coefficient.rmse |
the RMS error when |
Aaron Olsen
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
digitizeImages
,
reconstructStereoSets
This function has been replaced by digitizeImages
.
Aaron Olsen
This function opens an application in the user's default web browser for manually digitizing landmarks and Bezier curves from photographs.
digitizeImages(image.file, shapes.file=NULL, landmarks.file=NULL, control.points.file=NULL, curve.points.file=NULL, cal.file=NULL, landmarks.ref=NULL, curves.ref=NULL, image.id=NULL, landmark.color.blur = 'blue', landmark.color.focus = 'green', curve.color.blur = 'purple', control.point.color.blur = 'purple', control.point.color.focus = 'red', landmark.radius = 4, control.point.radius = 4, marker.stroke.width = 2, app.dir=NULL)
digitizeImages(image.file, shapes.file=NULL, landmarks.file=NULL, control.points.file=NULL, curve.points.file=NULL, cal.file=NULL, landmarks.ref=NULL, curves.ref=NULL, image.id=NULL, landmark.color.blur = 'blue', landmark.color.focus = 'green', curve.color.blur = 'purple', control.point.color.blur = 'purple', control.point.color.focus = 'red', landmark.radius = 4, control.point.radius = 4, marker.stroke.width = 2, app.dir=NULL)
image.file |
file path to the image or images to be digitized. This can be a folder containing one or more images or a vector of file paths of one or more images. |
shapes.file |
file path or folder indicating where shape files should be saved. This is a new format that is currently only intended to be used when collecting 2D data. The other input types ( |
landmarks.file |
same input as |
control.points.file |
same input as |
curve.points.file |
same input as |
cal.file |
file path to calibration file created by |
landmarks.ref |
landmarks to be digitized. This can either be a file path to a .txt file containing the landmarks (listed in a single column, each on a separate line) or a vector of landmark names. |
curves.ref |
curves to be digitized. For each curve, the name of the curve, the starting point and the ending point must be specified. |
image.id |
image IDs to be saved with each image. These will be used to reference shape data in the output of |
landmark.color.blur |
color of an unselected landmark. It might be necessary to change if the background color is close to the default. Colors must be valid SVG color names or codes (e.g. "hotpink", "#4B0082", etc.). A web-search for "SVG color codes" will indicate several possible options. |
landmark.color.focus |
color of a selected landmark. See |
curve.color.blur |
color of digitized curves. A different color for a selected curve is not yet supported. See |
control.point.color.blur |
color of an unselected control point. See |
control.point.color.focus |
color of a selected control point. See |
landmark.radius |
radius of digitized landmarks. |
control.point.radius |
radius of the Bezier control points. |
marker.stroke.width |
thickness of the lines used to draw the landmarks and control points. |
app.dir |
changes the shiny app directory for debugging. |
This function opens a digitizing app in the user's default browswer and allows for the digitization of landmarks and Bezier curves from photographs. Although the app runs in a web browser, the user does not have to be connected to the internet as the app runs on a local server. The R package 'shiny' handles the communication between the browser and the R console. Safari, Chrome and Opera all provide full compatibility with the apps's features. Please see StereoMorph tutorials for step-by-step tutorials on how to use StereoMorph for 2D or 3D shape data collection.
NULL
Aaron Olsen
This function returns the distances in grid units between pairs of points on a square grid. This function is used in testing the accuracy of a calibration by comparing theoretical distances among grid points to measured distances.
distanceGridUnits(pairs, nx)
distanceGridUnits(pairs, nx)
pairs |
a two-column matrix specifying the pairs of points between which the distance is to be found. |
nx |
the number of points in the first dimension along which grid points are counted (see "Details"). |
This function returns the distances in grid units between pairs of points on a square grid. Thus, adjoining points in the same row or column would be separated by a distance of one. The returned distances can then be multiplied by the grid square size to obtain the distances between pairs of points in real-world units (e.g. mm).
The input pairs
is a two-column matrix specifying the pairs of points, with the first column corresponding to one point and the second column to the other. The numbers in pairs
are indices of grid points (not point coordinates themselves). The assumed numbering scheme for the grid points is as follows: the points are first numbered across the first dimension (of length nx
) and then along a second dimension. For example, on a 5x4 grid points 1-5 would be across the first row, 6-10 across the second row, etc. For each row the point numbering starts in the same column. For distanceGridUnits()
the number of columns does not need to be specified since this can be found from the point index in pairs
. See "Examples" for an explanation of the numbering scheme.
a vector of the distances between the specified pairs of grid points.
Aaron Olsen
## INDICES OF POINT PAIRS ON A GRID WITH 5 ROWS pairs <- matrix(c(1,1, 5,10, 6,16, 1,20), nrow=4, ncol=2, byrow=TRUE) ## FIND THE DISTANCE BETWEEN PAIRS OF POINTS IN GRID UNITS ## NOTE LAST DISTANCE IS 5 BECAUSE IT IS A 3,4,5-TRIANGLE distanceGridUnits(pairs, nx=5) ## FOR ILLUSTRATION, HERE IS A GRID WITH 5 ROWS AND 4 COLUMNS xy <- cbind(rep(0:4, 4), c(rep(0, 5), rep(1, 5), rep(2, 5), rep(3, 5))) ## PLOT THESE POINTS plot(xy) ## PLOT LINE SEGMENTS CONNECTING THE PAIRS ABOVE segments(x0=xy[pairs[, 1], 1], y0=xy[pairs[, 1], 2], x1=xy[pairs[, 2], 1], y1=xy[pairs[, 2], 2], col=c('blue', 'red', 'purple', 'green'))
## INDICES OF POINT PAIRS ON A GRID WITH 5 ROWS pairs <- matrix(c(1,1, 5,10, 6,16, 1,20), nrow=4, ncol=2, byrow=TRUE) ## FIND THE DISTANCE BETWEEN PAIRS OF POINTS IN GRID UNITS ## NOTE LAST DISTANCE IS 5 BECAUSE IT IS A 3,4,5-TRIANGLE distanceGridUnits(pairs, nx=5) ## FOR ILLUSTRATION, HERE IS A GRID WITH 5 ROWS AND 4 COLUMNS xy <- cbind(rep(0:4, 4), c(rep(0, 5), rep(1, 5), rep(2, 5), rep(3, 5))) ## PLOT THESE POINTS plot(xy) ## PLOT LINE SEGMENTS CONNECTING THE PAIRS ABOVE segments(x0=xy[pairs[, 1], 1], y0=xy[pairs[, 1], 2], x1=xy[pairs[, 2], 1], y1=xy[pairs[, 2], 2], col=c('blue', 'red', 'purple', 'green'))
Finds the minimum distance between a point and a line or multiple points and a line in two or three dimensions.
distancePointToLine(p, l1, l2 = NULL)
distancePointToLine(p, l1, l2 = NULL)
p |
a vector of a single point or a matrix of multiple points |
l1 |
a vector describing a point on a line or a list with line constants |
l2 |
if |
If p
is a vector, the function returns the distance between a point and the line input. If p
is a matrix, the function returns the distance between each point in the matrix (defined by each row) and the line input. If p
is a vector, the length must be 2 or 3 (2D or 3D, respectively). If p
is a matrix, the number of columns must be 2 or 3 (2D or 3D, respectively).
The line input can be defined using one of three standard ways: two points on the line, 'm' and 'b' constants and direction numbers (a vector parallel to the line). If l1
is a vector, this is taken as one point on the line and l2
must be a second point on the line. If l1
is a list, the named objects must correspond to one of these three line definitions. Two points on the line are defined as l1$l1
and l1$l2
. 'm' and 'b' are defined as l1$m
and l1$b
. And the direction numbers 'abc' are defined as l1$a
, l1$b
and l1$c
.
a vector of distance(s)
Aaron Olsen
## FIND THE DISTANCE BETWEEN A 2D POINT AND A LINE DEFINED BY A SLOPE AND Y-INTERCEPT distancePointToLine(p=c(0, 2), l1=list(m=0, b=1)) ## FIND THE DISTANCE BETWEEN A 2D POINT AND A LINE DEFINED BY TWO POINTS ON THE LINE distancePointToLine(p=c(0, 5), l1=list(l1=c(2, 4), l2=c(2, 1))) ## FIND THE DISTANCE BETWEEN MULTIPLE 2D POINTS AND A LINE DEFINED BY A SLOPE AND Y-INTERCEPT p <- matrix(c(0, 0, 1, 1, 2, 2), nrow=3, ncol=2, byrow=TRUE) distancePointToLine(p=p, l1=list(m=0, b=1)) ## FIND THE DISTANCE BETWEEN MULTIPLE 2D POINTS AND A LINE DEFINED BY DIRECTION NUMBERS p <- matrix(c(0, -1.5, 1, -2, 2, 2), nrow=3, ncol=2, byrow=TRUE) distancePointToLine(p=p, l1=list(a=1, b=2, c=3)) ## FIND THE DISTANCE BETWEEN A 3D POINT AND A LINE DEFINED BY TWO POINTS ON THE LINE ## HERE THE DISTANCE IS EQUAL TO sqrt(2) distancePointToLine(p=c(1, 1, 1), l1=c(0, 0, 0), l2=c(1, 0, 0)) ## FIND THE DISTANCE BETWEEN MULTIPLE 3D POINTS AND A LINE DEFINED BY TWO POINTS ON THE LINE p <- matrix(c(0, 0, 0, 1, 1, 1, 2, 2, 2), nrow=3, ncol=3, byrow=TRUE) distancePointToLine(p=p, l1=list(l1=c(0, 0, 0), l2=c(1, 0, 0)))
## FIND THE DISTANCE BETWEEN A 2D POINT AND A LINE DEFINED BY A SLOPE AND Y-INTERCEPT distancePointToLine(p=c(0, 2), l1=list(m=0, b=1)) ## FIND THE DISTANCE BETWEEN A 2D POINT AND A LINE DEFINED BY TWO POINTS ON THE LINE distancePointToLine(p=c(0, 5), l1=list(l1=c(2, 4), l2=c(2, 1))) ## FIND THE DISTANCE BETWEEN MULTIPLE 2D POINTS AND A LINE DEFINED BY A SLOPE AND Y-INTERCEPT p <- matrix(c(0, 0, 1, 1, 2, 2), nrow=3, ncol=2, byrow=TRUE) distancePointToLine(p=p, l1=list(m=0, b=1)) ## FIND THE DISTANCE BETWEEN MULTIPLE 2D POINTS AND A LINE DEFINED BY DIRECTION NUMBERS p <- matrix(c(0, -1.5, 1, -2, 2, 2), nrow=3, ncol=2, byrow=TRUE) distancePointToLine(p=p, l1=list(a=1, b=2, c=3)) ## FIND THE DISTANCE BETWEEN A 3D POINT AND A LINE DEFINED BY TWO POINTS ON THE LINE ## HERE THE DISTANCE IS EQUAL TO sqrt(2) distancePointToLine(p=c(1, 1, 1), l1=c(0, 0, 0), l2=c(1, 0, 0)) ## FIND THE DISTANCE BETWEEN MULTIPLE 3D POINTS AND A LINE DEFINED BY TWO POINTS ON THE LINE p <- matrix(c(0, 0, 0, 1, 1, 1, 2, 2, 2), nrow=3, ncol=3, byrow=TRUE) distancePointToLine(p=p, l1=list(l1=c(0, 0, 0), l2=c(1, 0, 0)))
Finds the distance betweeen two single points, the distances between one point and a set of points, or the distances between two point sets. Points can be of any number of dimensions.
distancePointToPoint(p1, p2 = NULL)
distancePointToPoint(p1, p2 = NULL)
p1 |
a vector of a single point or a matrix of one or multiple points |
p2 |
a vector of a single point or a matrix of one or multiple points. If |
If p1
is a single point and p2
is a single point then the function returns the distance between these two points. If either p1
or p2
is a single point and the other is a matrix of multiple points then the function returns a vector of the distances between the single point and each of the multiple points. If both p1
and p2
are matrices of multiple points, then the function returns a vector of the distances between the points in each corresponding row. If p1
and p2
are both matrices, the matrix dimensions must match.
If p2
is NULL
, then distancePointToPoint()
returns the distance between consecutive points in p1
. If p1
is a vector, the function returns the absolute difference between consecutive values of p1
(interpoint distances along a single dimension). If p1
is a matrix, then the function returns the distance between the point in each row of p1
and its subsequent row. This can be used to return the interpoint distances along a curve defined as a matrix of points.
a vector of distance(s)
Aaron Olsen
## FIND THE DISTANCE BETWEEN TWO, 2D POINTS ## VALUE IS sqrt(2) distancePointToPoint(p1=c(0, 0), p2=c(1, 1)) ## FIND THE DISTANCE BETWEEN A 2D POINT AND MULTIPLE 2D POINTS p1 <- c(0, 0) p2 <- matrix(c(1, 1, 2, 2, 3, 3), nrow=3, ncol=2, byrow=TRUE) distancePointToPoint(p1=p1, p2=p2) ## FIND THE DISTANCE BETWEEN TWO SETS OF 2D POINTS p1 <- matrix(c(0, 0, 1, 1, 2, 2), nrow=3, ncol=2, byrow=TRUE) p2 <- matrix(c(1, 1, 2, 2, 3, 3), nrow=3, ncol=2, byrow=TRUE) distancePointToPoint(p1=p1, p2=p2) ## FIND THE DISTANCE BETWEEN A 3D POINT AND MULTIPLE 3D POINTS p1 <- c(0, 0, 0) p2 <- matrix(c(1, 1, 1, 2, 2, 2, 3, 3, 3), nrow=3, ncol=3, byrow=TRUE) distancePointToPoint(p1=p1, p2=p2) ## FIND THE DISTANCE BETWEEN CONSECUTIVE VALUES IN A VECTOR distancePointToPoint(p1=c(1, 2, 4, 7)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 2D POINTS IN A MATRIX ## HERE, WE FIND THE DISTANCE BETWEEN THE POINT c(0, 0) AND c(1, 1), WHICH IS sqrt(2) distancePointToPoint(p1=matrix(c(0, 0, 1, 1), nrow=2, ncol=2, byrow=TRUE)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 2D POINTS IN A MATRIX, WITH MORE POINTS ## HERE, WE ADD TWO MORE POINTS TO THE PREVIOUS EXAMPLE: c(2, 2) AND c(3, 3) ## THE DISTANCE BETWEEN EACH CONSECUTIVE PAIR OF POINTS IS sqrt(2) distancePointToPoint(p1=matrix(c(0, 0, 1, 1, 2, 2, 3, 3), nrow=4, ncol=2, byrow=TRUE)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 3D POINTS IN A MATRIX distancePointToPoint(p1=matrix(c(0, 0, 0, 1, 1, 1), nrow=2, ncol=3, byrow=TRUE)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 4D POINTS IN A MATRIX distancePointToPoint(p1=matrix(c(0, 0, 0, 0, 1, 1, 1, 1), nrow=2, ncol=4, byrow=TRUE))
## FIND THE DISTANCE BETWEEN TWO, 2D POINTS ## VALUE IS sqrt(2) distancePointToPoint(p1=c(0, 0), p2=c(1, 1)) ## FIND THE DISTANCE BETWEEN A 2D POINT AND MULTIPLE 2D POINTS p1 <- c(0, 0) p2 <- matrix(c(1, 1, 2, 2, 3, 3), nrow=3, ncol=2, byrow=TRUE) distancePointToPoint(p1=p1, p2=p2) ## FIND THE DISTANCE BETWEEN TWO SETS OF 2D POINTS p1 <- matrix(c(0, 0, 1, 1, 2, 2), nrow=3, ncol=2, byrow=TRUE) p2 <- matrix(c(1, 1, 2, 2, 3, 3), nrow=3, ncol=2, byrow=TRUE) distancePointToPoint(p1=p1, p2=p2) ## FIND THE DISTANCE BETWEEN A 3D POINT AND MULTIPLE 3D POINTS p1 <- c(0, 0, 0) p2 <- matrix(c(1, 1, 1, 2, 2, 2, 3, 3, 3), nrow=3, ncol=3, byrow=TRUE) distancePointToPoint(p1=p1, p2=p2) ## FIND THE DISTANCE BETWEEN CONSECUTIVE VALUES IN A VECTOR distancePointToPoint(p1=c(1, 2, 4, 7)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 2D POINTS IN A MATRIX ## HERE, WE FIND THE DISTANCE BETWEEN THE POINT c(0, 0) AND c(1, 1), WHICH IS sqrt(2) distancePointToPoint(p1=matrix(c(0, 0, 1, 1), nrow=2, ncol=2, byrow=TRUE)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 2D POINTS IN A MATRIX, WITH MORE POINTS ## HERE, WE ADD TWO MORE POINTS TO THE PREVIOUS EXAMPLE: c(2, 2) AND c(3, 3) ## THE DISTANCE BETWEEN EACH CONSECUTIVE PAIR OF POINTS IS sqrt(2) distancePointToPoint(p1=matrix(c(0, 0, 1, 1, 2, 2, 3, 3), nrow=4, ncol=2, byrow=TRUE)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 3D POINTS IN A MATRIX distancePointToPoint(p1=matrix(c(0, 0, 0, 1, 1, 1), nrow=2, ncol=3, byrow=TRUE)) ## FIND THE DISTANCE BETWEEN CONSECUTIVE 4D POINTS IN A MATRIX distancePointToPoint(p1=matrix(c(0, 0, 0, 0, 1, 1, 1, 1), nrow=2, ncol=4, byrow=TRUE))
This function uses the corners from a grid positioned in several different orientations within a stereo camera setup to estimate the DLT calibration coefficients that minimize reconstruction error.
dltCalibrateCameras(coor.2d, nx, grid.size, c.run = FALSE, reduce.grid.dim = 3, fit.min.break = 1, nlm.iter.max.init = 100, objective.min.init = 10, nlm.eval.max = 350, nlm.iter.max = 250, nlm.calls.max = 100, min.views = 'max', objective.min = 1, grid.incl.min=2, objective.min.break = NULL, start.param=NULL, sx = NULL, sy = NULL, print.progress = FALSE, print.tab = '') ## S3 method for class 'dltCalibrateCameras' summary(object, ...)
dltCalibrateCameras(coor.2d, nx, grid.size, c.run = FALSE, reduce.grid.dim = 3, fit.min.break = 1, nlm.iter.max.init = 100, objective.min.init = 10, nlm.eval.max = 350, nlm.iter.max = 250, nlm.calls.max = 100, min.views = 'max', objective.min = 1, grid.incl.min=2, objective.min.break = NULL, start.param=NULL, sx = NULL, sy = NULL, print.progress = FALSE, print.tab = '') ## S3 method for class 'dltCalibrateCameras' summary(object, ...)
coor.2d |
a four-dimensional array of grid points. The first two dimensions correspond to each matrix of grid points, the third corresponds to each grid position/orientation and the fourth corresponds to each camera view. Can be read from file by the function |
nx |
the number of points along the first dimension (e.g. this would be the number of points in each row if points in |
grid.size |
the size of the grid squares in real-world units (e.g. millimeters). |
c.run |
a logical indicating whether a second optimization should be performed on the calibration coefficients. |
reduce.grid.dim |
a numeric indicating the number of grid points along each dimension for each grid after resampling. The total number of resampled points is |
fit.min.break |
passed to |
nlm.iter.max.init |
The maximum number of iterations to be performed by |
objective.min.init |
The objective used during the initial coefficient optimization, passed as a control parameter to |
nlm.eval.max |
The maximum number of evaluations to be performed by |
nlm.iter.max |
The maximum number of iterations to be performed by |
nlm.calls.max |
The maximum number of different sets of random starting parameters to use during coefficient optimization. This parameter cannot exceed 576. |
min.views |
The minimum views in which corners must be detected in order to use in coefficient estimation. If set to 'max' (default) this will be equal to the number of input views. |
objective.min |
The expected mean reconstruction error when optimizing the calibration coefficients (the minimum, or |
grid.incl.min |
The minimum number of grids to include during coefficient optimization. |
objective.min.break |
During coefficient optimization if the error (in pixels) always exceeds this value the estimation will stop estimating the position of additional checkerboards. |
start.param |
An set of fixed starting parameters to be used during coefficient optimization. This parameter is intended primarily for debugging. |
sx |
Used for de-bugging. |
sy |
Used for de-bugging. |
print.progress |
a logical indicating whether the progress of the function should be printed while running. This includes the error in grid re-sampling, an iteration count during optimization and other outputs relating to the optimization. |
print.tab |
Tabs preceding lines printed to console. |
object |
a list of class |
... |
further arguments passed to or from other methods. |
Calibration is the most challenging step in stereo camera data collection. Most fundamentally, DLT calibration requires a set of 3D coordinates and their corresponding 2D pixel coordinates in each camera view in order to derive calibration coefficients (see dltCoefficients
). These coefficients can then be used to reconstruct any point in 3D given its 2D pixel coordinates in two or more camera views. DLT calibration has traditionally been done using a "calibration object", typically a 3D box-shaped structure filled with markers at known 3D positions. Such objects require the use of high precision machining in order to achieve an accurate calibration and the calibration points are usually digitized manually.
The dltCalibrateCameras()
function provides a camera calibration routine that is easier to implement and potentially more accurate. This function uses the corners from a grid positioned in several different orientations within the calibration space to estimate the DLT calibration coefficients that minimize reconstruction error. The easiest method for obtaining these corner points is to print a checkerboard pattern (using drawCheckerboard
), attach the pattern to flat, hard surface and use findCheckerboardCorners
to automatically extract the pixel coordinates of the internal corners.
The grid pattern should be photographed in at least four different positions and orientations spanning the volume to be calibrated (the tutorial files loaded with StereoMorph include eight different positions). Using only a couple of positions will result in uneven sampling of the calibration volume causing larger errors in some regions relative to others. Additionally, using only a single orientation of the checkerboard will produce higher errors along a particular dimension relative to the others. Once the pixel coordinates of the grid points (e.g. the internal corners of the checkerboard pattern) have been extracted from all of the calibration images, they should be read into an array using readCheckerboardsToArray
. This function allows for the point order to be reversed along rows, columns or both. If one of the cameras views the pattern upside down relative to another camera or if the pattern is in a different orientation, the grid points may be extracted in a different order. This can be fixed using the row.reverse
and col.reverse
arguments in readCheckerboardsToArray
. It is essential that the grid points extracted from each camera view correspond to each other row-by-row or else the calibration will not work.
dltCalibrateCameras()
first calls resampleGridImagePoints
to downsample the number of grid points. reduce.grid.dim
is the downsample number (the default is 3, meaning 3x3 or nine points per grid). Downsampling can be turned off by setting reduce.grid.dim
to 0
, although this is not recommended as it will increase run-time substantially without increasing accuracy. A camera perspective model is fit to the full point set such that the number of points input to the coefficient optimization can be reduced (thereby reducing run-time) without losing any relevant information (see resampleGridImagePoints
). If print.progress
is set to TRUE
, the mean and maximum fit error is printed for each input grid. As the fitting does not take into account lens distortion, high fit errors may indicate large distortional effects.
Since each checkerboard grid has been photographed in an arbitrary position and orientation, the 3D coordinates of the grid points are unknown. However, if the first grid is fixed, each additional grid can be described by applying six transformation parameters relative to the first (three translational and three rotational). Using the reduced grid point set, dltCalibrateCameras()
uses nlminb()
to search for the six transformation parameters per grid that minimizes the RMS error when the 3D coordinates are input (with the corresponding 2D coordinates) to dltCoefficients
. In effect, dltCalibrateCameras()
solves for the position of each grid in 3D space using the error from dltCoefficients
as an optimality criterion. Since the first grid is fixed, the optimization will search for 6*(n-1) parameters, where n is the number of separate grid orientations. nlminb()
calls the function dltTransformationParameterRMSError
.
In order to fully explore the parameter space, dltCalibrateCameras()
calls nlminb()
several times with a different set of randomly generated starting parameters to estimate the transformation parameters for each additional grid. The number of different sets of starting parameters is determined by nlm.calls.max
. An initial optimization run is intended to quickly determine whether a particular set of starting parameters is likely to lead to convergence. The number of iterations for this initial optimization is determined by nlm.iter.max.init
and the objective used in determining likely convergence is objective.min.init
. If it is determined that the starting parameters are likely to lead to convergence below objective.min
, nlminb()
is allowed to continue optimizing. For each grid, the solution yielding the lowest error, or the first solution below the objective.min
threshold, is retained for the next grid optimization.
These optimal transformation parameters are then used to obtain the 3D coordinates of the original grid points (not downsampled). Once these 3D coordinates are known, the 3D and 2D pixel coordinates are input to dltCoefficients
to obtain the 11 calibration coefficients per camera. For this reason, the calibration coefficient RMS Error (coefficient.rmse
) returned will differ slightly from the reported final nlminb()
minimum (t.min
).
If c.run
is set to TRUE
, dltCalibrateCameras()
performs a second optimization on the calibration coefficients themselves. nlminb()
is used, this time calling dltCoefficientRMSError
, to find the 11 calibration coefficients per view that minimizes the reconstruction RMS error. Note that dltCoefficients
cannot be used as with the previous optimization because the coefficients must be an input. Running this second optimization seems to have little effect in increasing the accuracy of the calibration but is included as this may be useful for some stereo setups.
a list of class "dltCalibrateCameras"
with the following elements:
cal.coeff |
a matrix of 11 optimized DLT calibration coefficients per camera view. |
coor.3d |
the optimized 3D coordinates of the input grid points in |
mean.reconstruct.rmse |
the RMS error when |
coefficient.rmse |
the RMS error when |
t.param.final |
the final transformation parameters reported by |
t.min |
the minimum reported by |
t.runtime |
the run-time (in seconds) for the first optimization. |
if c.run
is FALSE
, the following are NA
. Otherwise,
c.param.init |
the initial parameters for the second optimization. |
c.param.final |
the final parameters reported by |
c.min |
the minimum reported by |
c.iter |
the number of iterations reported by |
c.runtime |
the run-time (in seconds) for the second optimization. |
Aaron Olsen
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltTestCalibration
,
dltCoefficients
,
readCheckerboardsToArray
,
transformPlanarCalibrationCoordinates
,
dltTransformationParameterRMSError
,
## SET NUMBER OF INTERNAL CORNERS FOR CALIBRATION GRIDS nx <- 21 ny <- 14 ## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CHECKERBOARD CORNERS FROM CALIBRATION IMAGE SET ## THE TUTORIAL INCLUDES 8 CALIBRATION IMAGES FROM TWO CAMERA VIEWS file <- matrix(c(paste0(fdir, "cal_a", 1:8, "_v1.txt"), paste0(fdir, "cal_a", 1:8, "_v2.txt")), ncol=2) ## READ IN CHECKERBOARD CORNERS ## NOTE THAT col.reverse IS USED TO MAKE POINTS CORRESPOND coor.2d <- readCheckerboardsToArray(file=file, nx=nx, ny=ny, col.reverse=TRUE) ## SET GRID SIZE (IN MM) grid.size <- 6.347889 ## Not run: ## CALIBRATE CAMERAS ## TO REDUCE RUN-TIME, WE JUST USE CORNERS FROM TWO IMAGES (1 AND 5) dlt_calibrate_cameras <- dltCalibrateCameras(coor.2d=coor.2d[, , c(1, 5), ], nx=nx, grid.size=grid.size, c.run=FALSE, print.progress=TRUE) ## RUN CALIBRATION ON ALL IMAGES, ACCURACY IS GREATLY IMPROVED dlt_calibrate_cameras <- dltCalibrateCameras(coor.2d=coor.2d, nx=nx, grid.size=grid.size, c.run=FALSE, print.progress=TRUE) ## PRINT SUMMARY summary(dlt_calibrate_cameras) ## End(Not run)
## SET NUMBER OF INTERNAL CORNERS FOR CALIBRATION GRIDS nx <- 21 ny <- 14 ## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CHECKERBOARD CORNERS FROM CALIBRATION IMAGE SET ## THE TUTORIAL INCLUDES 8 CALIBRATION IMAGES FROM TWO CAMERA VIEWS file <- matrix(c(paste0(fdir, "cal_a", 1:8, "_v1.txt"), paste0(fdir, "cal_a", 1:8, "_v2.txt")), ncol=2) ## READ IN CHECKERBOARD CORNERS ## NOTE THAT col.reverse IS USED TO MAKE POINTS CORRESPOND coor.2d <- readCheckerboardsToArray(file=file, nx=nx, ny=ny, col.reverse=TRUE) ## SET GRID SIZE (IN MM) grid.size <- 6.347889 ## Not run: ## CALIBRATE CAMERAS ## TO REDUCE RUN-TIME, WE JUST USE CORNERS FROM TWO IMAGES (1 AND 5) dlt_calibrate_cameras <- dltCalibrateCameras(coor.2d=coor.2d[, , c(1, 5), ], nx=nx, grid.size=grid.size, c.run=FALSE, print.progress=TRUE) ## RUN CALIBRATION ON ALL IMAGES, ACCURACY IS GREATLY IMPROVED dlt_calibrate_cameras <- dltCalibrateCameras(coor.2d=coor.2d, nx=nx, grid.size=grid.size, c.run=FALSE, print.progress=TRUE) ## PRINT SUMMARY summary(dlt_calibrate_cameras) ## End(Not run)
Returns the RMS error from dltReconstruct
when optimizing the calibration coefficients. This function is called internally by dltCalibrateCameras
.
dltCoefficientRMSError(p, coor.2d)
dltCoefficientRMSError(p, coor.2d)
p |
a vector of the current, 11-parameter calibration coefficients. |
coor.2d |
a four-dimensional array of grid points passed from |
the mean RMS error from dltReconstruct
across all views.
Aaron Olsen
transformPlanarCalibrationCoordinates
,
dltReconstruct
,
dltCalibrateCameras
This function takes 3D coordinates and their corresponding 2D coordinates in one or more camera views and returns DLT calibration coefficients. The DLT coefficients can then be used in 3D reconstruction and calculation of epipolar lines.
dltCoefficients(coor.3d, coor.2d)
dltCoefficients(coor.3d, coor.2d)
coor.3d |
a three-column matrix of 3D coordinates. |
coor.2d |
an three-dimensional array of 2D pixel coordinates. |
This function takes 3D coordinates and their corresponding 2D coordinates in one or more camera views and returns DLT calibration coefficients. Note that to find the calibration coefficient for a particular camera view, only the pixel coordinates in that camera view and their corresponding 3D coordinates are used. Thus, it is possible to derive calibration coefficients for several cameras without any overlap among the views in the points used to derive the calibration coefficients. Any missing values (either in coor.3d
or pixel coordinates missing in a particular view in coor.2d
) can be input as NA
; they will be ignored.
The requirements for the structure of the coor.2d
array are as follows. The first dimension of coor.2d
is the number of points used in calculating the DLT coefficients. The number of elements in the first dimension of coor.2d
must match the number of rows in coor.3d
and these must be corresponding points (though some may be NA
). The second dimension of coor.2d
should be two as these are x,y-coordinates. The third dimension of coor.2d
is the number of camera views. This will correspond to the number of columns in the returned calibration coefficient matrix.
a list of class "dltCoefficients"
with the following elements:
cal.coeff |
the calibration coefficient matrix. |
rmse |
the root-mean-square error for each camera view. |
This function was modified by A Olsen from the Matlab function dlt_reconstruct()
written by T Hedrick.
Aaron Olsen
Abdel-Aziz, Y.I., Karara, H.M. (1971) Direct linear transformation into object space coordinates in close-range photogrammetry. Proc. Symp. on Close-Range Photogrammetry (University of Illinois at Urbana-Champaign).
Hedrick, T.L. (2008) Software techniques for two- and three-dimensional kinematic measurements of biological and biomimetic systems. Bioinspiration & Biomimetics, 3 (034001).
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltCalibrateCameras
, findCheckerboardCorners
## SET NUMBER OF INTERNAL CORNERS AND ROWS nx <- 21 ny <- 14 ## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET FILE PATHS FOR PIXEL COORDINATES FROM CALIBRATION IMAGES 1 AND 5 file2d <- matrix(c(paste0(fdir, "cal_a", c(1, 5), "_v1.txt"), paste0(fdir, "cal_a", c(1, 5), "_v2.txt")), ncol=2) ## READ INTO ARRAY ## THESE ARE THE 2D COORDINATES USED IN THE CALIBRATION coor.2d <- readCheckerboardsToArray(file=file2d, nx=nx, ny=ny, col.reverse=TRUE) ## REDUCE ARRAY DIMENSIONS TO THREE ## THIS STACKS MULTIPLE VIEWS ON TOP OF EACH OTHER INTO THE SAME MATRIX coor.2d <- apply(coor.2d, c(2, 4), matrix, byrow=FALSE) ## GET FILE PATH FOR CORRESPONDING 3D COORDINATES file3d <- paste0(fdir, "cal_3d_coor.txt") ## READ 3D POINTS INTO MATRIX coor.3d <- as.matrix(read.table(file=file3d)) ## FIND THE DLT COEFFICIENTS dlt_coeffcients <- dltCoefficients(coor.3d=coor.3d, coor.2d=coor.2d) ## PRINT THE SUMMARY summary(dlt_coeffcients) ## NOTE THAT EACH CAMERA VIEW IS CALIBRATED SEPARATELY ## GIVES THE EXACT SAME RESULT dltCoefficients(coor.3d=coor.3d, coor.2d=coor.2d[, , 1])
## SET NUMBER OF INTERNAL CORNERS AND ROWS nx <- 21 ny <- 14 ## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET FILE PATHS FOR PIXEL COORDINATES FROM CALIBRATION IMAGES 1 AND 5 file2d <- matrix(c(paste0(fdir, "cal_a", c(1, 5), "_v1.txt"), paste0(fdir, "cal_a", c(1, 5), "_v2.txt")), ncol=2) ## READ INTO ARRAY ## THESE ARE THE 2D COORDINATES USED IN THE CALIBRATION coor.2d <- readCheckerboardsToArray(file=file2d, nx=nx, ny=ny, col.reverse=TRUE) ## REDUCE ARRAY DIMENSIONS TO THREE ## THIS STACKS MULTIPLE VIEWS ON TOP OF EACH OTHER INTO THE SAME MATRIX coor.2d <- apply(coor.2d, c(2, 4), matrix, byrow=FALSE) ## GET FILE PATH FOR CORRESPONDING 3D COORDINATES file3d <- paste0(fdir, "cal_3d_coor.txt") ## READ 3D POINTS INTO MATRIX coor.3d <- as.matrix(read.table(file=file3d)) ## FIND THE DLT COEFFICIENTS dlt_coeffcients <- dltCoefficients(coor.3d=coor.3d, coor.2d=coor.2d) ## PRINT THE SUMMARY summary(dlt_coeffcients) ## NOTE THAT EACH CAMERA VIEW IS CALIBRATED SEPARATELY ## GIVES THE EXACT SAME RESULT dltCoefficients(coor.3d=coor.3d, coor.2d=coor.2d[, , 1])
Given the same point in two camera views, this function finds the distance between the point in the second camera view and a epipolar line in the second view, as determined from the point in the first view. The option is also available to return the mean reciprocal epipolar distance.
dltEpipolarDistance(p1, p2, cal.coeff, reciprocal = FALSE)
dltEpipolarDistance(p1, p2, cal.coeff, reciprocal = FALSE)
p1 |
an x,y vector or two-column matrix of a point or points in the camera view corresponding to the first column of |
p2 |
an x,y vector or two-column matrix of a point or points in a second camera view, corresponding to the second column of |
cal.coeff |
a two-column matrix of DLT calibration coefficients, where each column corresponds to the views from which |
reciprocal |
a logical indicating whether epipolar distance should be calculated reciprocally and then averaged. |
In a stereo camera setup, a point in one camera view must fall somewhere along a line in a second camera view. This line is called its epipolar line. Due to error in manually selecting the same point in two camera views and error in the calibration, the epipolar line of the point in the first view will not intersect exactly with the same point in the second view. This distance between a point and the epipolar line of the same point in another view is the epipolar distance (or error).
The epipolar distance can be calculated between the point in the second view and the epipolar line of the point in the first view or between the point in the first view and the epipolar line of the point in the second view; the choice is arbitrary. This function performs the former. If a user would like to perform the latter, simply switch p1
with p2
and reverse the column order of cal.coeff
(see "Examples"). Another possibility is to perform both distance calculations and return an average (mean reciprocal epipolar distance). This can be done by setting reciprocal
to TRUE
.
Although a stereo camera system may consist of more than two cameras, the coefficients of only two cameras should be input to dltEpipolarDistance()
. Only the coefficients of the two camera views for which epipolar distances are being calculated are relevant. Currently, this function only works with the 11-parameter DLT model.
A few options for input of p1
and p2
are available. If a single point is input for both, the epipolar distance is calculated for these two points. If a matrix of points is input for both (of the same dimensions), the epipolar distance is calculated pair-wise - points in the same row are treated as the same point. Lastly, if a single point is input as p1
and a matrix is input as p2
, the epipolar distance is calculated for p1
relative to all points in p2
(see "Examples").
a vector of the epipolar distance(s).
Aaron Olsen
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltCalibrateCameras
, dltEpipolarLine
, dltNearestPointOnEpipolar
,
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_files <- paste0(fdir, c("lm_2d_a1_v1.txt", "lm_2d_a1_v2.txt")) ## READ LANDMARKS INTO MATRIX lm.array <- readLandmarksToArray(file=lm_files, row.names=1) ## FIND EPIPOLAR DISTANCE BETWEEN TWO SINGLE LANDMARKS ## EPIPOLAR DISTANCE (ERROR) IS AROUND 7 PIXELS ## IDENTIFYING THE EXACT SAME POINT IN TWO VIEWS MANUALLY IS CHALLENGING... dltEpipolarDistance(p1=lm.array[1, , 1], p2=lm.array[1, , 2], cal.coeff=cal.coeff) ## FIND EPIPOLAR DISTANCE USING EPIPOLAR FROM SECOND VIEW INSTEAD dltEpipolarDistance(p1=lm.array[1, , 2], p2=lm.array[1, , 1], cal.coeff=cal.coeff[, 2:1]) ## FIND MEAN RECIPROCAL EPIPOLAR DISTANCE BETWEEN TWO SINGLE LANDMARKS ## THIS IS THE AVERAGE OF THE PREVIOUS TWO DISTANCES dltEpipolarDistance(p1=lm.array[1, , 1], p2=lm.array[1, , 2], cal.coeff=cal.coeff, reciprocal=TRUE) ## FIND EPIPOLAR DISTANCES BETWEEN ALL LANDMARKS ## PROCEEDS PAIRWISE BECAUSE p1 AND p2 HAVE THE SAME DIMENSIONS dltEpipolarDistance(p1=lm.array[, , 1], p2=lm.array[, , 2], cal.coeff=cal.coeff) ## FIND EPIPOLAR DISTANCES BETWEEN FIRST LANDMARK AND ALL LANDMARKS ## HERE THE EPIPOLAR DISTANCES ARE HIGH BECAUSE ONLY THE FIRST LANDMARK ## CORRESPONDS ## THE REMAINING POINTS ARE NOT THE SAME LANDMARK dltEpipolarDistance(p1=lm.array[1, , 1], p2=lm.array[, , 2], cal.coeff=cal.coeff)
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_files <- paste0(fdir, c("lm_2d_a1_v1.txt", "lm_2d_a1_v2.txt")) ## READ LANDMARKS INTO MATRIX lm.array <- readLandmarksToArray(file=lm_files, row.names=1) ## FIND EPIPOLAR DISTANCE BETWEEN TWO SINGLE LANDMARKS ## EPIPOLAR DISTANCE (ERROR) IS AROUND 7 PIXELS ## IDENTIFYING THE EXACT SAME POINT IN TWO VIEWS MANUALLY IS CHALLENGING... dltEpipolarDistance(p1=lm.array[1, , 1], p2=lm.array[1, , 2], cal.coeff=cal.coeff) ## FIND EPIPOLAR DISTANCE USING EPIPOLAR FROM SECOND VIEW INSTEAD dltEpipolarDistance(p1=lm.array[1, , 2], p2=lm.array[1, , 1], cal.coeff=cal.coeff[, 2:1]) ## FIND MEAN RECIPROCAL EPIPOLAR DISTANCE BETWEEN TWO SINGLE LANDMARKS ## THIS IS THE AVERAGE OF THE PREVIOUS TWO DISTANCES dltEpipolarDistance(p1=lm.array[1, , 1], p2=lm.array[1, , 2], cal.coeff=cal.coeff, reciprocal=TRUE) ## FIND EPIPOLAR DISTANCES BETWEEN ALL LANDMARKS ## PROCEEDS PAIRWISE BECAUSE p1 AND p2 HAVE THE SAME DIMENSIONS dltEpipolarDistance(p1=lm.array[, , 1], p2=lm.array[, , 2], cal.coeff=cal.coeff) ## FIND EPIPOLAR DISTANCES BETWEEN FIRST LANDMARK AND ALL LANDMARKS ## HERE THE EPIPOLAR DISTANCES ARE HIGH BECAUSE ONLY THE FIRST LANDMARK ## CORRESPONDS ## THE REMAINING POINTS ARE NOT THE SAME LANDMARK dltEpipolarDistance(p1=lm.array[1, , 1], p2=lm.array[, , 2], cal.coeff=cal.coeff)
This function takes a point in one camera view and returns either its epipolar line in another camera view or its epipolar line in that same camera view (self-epipolar line).
dltEpipolarLine(p, cal.coeff1, cal.coeff2 = NULL, self = FALSE)
dltEpipolarLine(p, cal.coeff1, cal.coeff2 = NULL, self = FALSE)
p |
vector of x,y pixel coordinates for a point in an image. |
cal.coeff1 |
DLT calibration coefficients corresponding to the camera view from which |
cal.coeff2 |
in the case that |
self |
a logical indicating whether the epipolar line returned should be a self-epipolar line. |
In a stereo camera setup, a point in one camera view must fall somewhere along a line in a second camera view. This line is called its epipolar line. If a second point is taken anywhere along this epipolar line in the second camera and its epipolar line is found in the first camera, the original point must fall along this line. The epipolar line in the first camera view, along which the original point falls, is called its self-epipolar line (Yakutieli et al. 2007). dltEpipolarLine()
uses DLT calibration coefficients (see dltCalibrateCameras
) to find the epipolar or self-epipolar line for a given point in a stereo camera setup.
Although a stereo camera system may consist of more than two cameras, the coefficients of only two cameras should be input to dltEpipolarLine()
. Only the coefficients of the two cameras between which epipolar lines are being calculated are relevant. These two columns of coefficients can be input as one matrix (to cal.coeff1
) or as two separate, one-column matrices (to cal.coeff1
and cal.coeff2
).
Currently, dltEpipolarLine()
only works with the 11-parameter DLT model.
dltEpipolarLine()
outputs the resulting epipolar line in two forms: slope-intercept coefficients (m
and b
) and two points on the line (l1
and l2
). These are stored in a list as follows:
m |
the slope of the epipolar line. |
b |
the y-intercept of the epipolar line. |
l1 |
one point on the epipolar line. |
l2 |
a second point on the epipolar line. |
This function was modified by A Olsen from the Matlab function partialdlt()
written by T Hedrick. A Olsen added the self-epipolar functionality after Yekutieli et al. 2007.
Aaron Olsen
Abdel-Aziz, Y.I., Karara, H.M. (1971) Direct linear transformation into object space coordinates in close-range photogrammetry. Proc. Symp. on Close-Range Photogrammetry (University of Illinois at Urbana-Champaign).
Yekutieli, Y., Mitelman, R., Hochner, B. & Flash, T. (2007). Analyzing Octopus Movements Using Three-Dimensional Reconstruction. Journal of Neurophysiology, 98, 1775–1790.
Hedrick, T.L. (2008) Software techniques for two- and three-dimensional kinematic measurements of biological and biomimetic systems. Bioinspiration & Biomimetics, 3 (034001).
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltCalibrateCameras
, dltEpipolarDistance
, dltNearestPointOnEpipolar
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_file <- paste0(fdir, "lm_2d_a1_v1.txt") ## READ LANDMARKS INTO MATRIX lm.matrix <- readLandmarksToMatrix(file=lm_file, row.names=1) ## FIND EPIPOLAR LINE IN SECOND CAMERA VIEW epipolar <- dltEpipolarLine(p=lm.matrix['occipital_condyle', ], cal.coeff1=cal.coeff) ## FIND SELF-EPIPOLAR LINE (IN FIRST CAMERA VIEW) self_epipolar <- dltEpipolarLine(p=lm.matrix['occipital_condyle', ], cal.coeff1=cal.coeff, self=TRUE) ## CONFIRM THAT DISTANCE FROM ORIGINAL POINT TO SELF-EPIPOLAR LINE IS ZERO distancePointToLine(p=lm.matrix['occipital_condyle', ], l1=self_epipolar)
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_file <- paste0(fdir, "lm_2d_a1_v1.txt") ## READ LANDMARKS INTO MATRIX lm.matrix <- readLandmarksToMatrix(file=lm_file, row.names=1) ## FIND EPIPOLAR LINE IN SECOND CAMERA VIEW epipolar <- dltEpipolarLine(p=lm.matrix['occipital_condyle', ], cal.coeff1=cal.coeff) ## FIND SELF-EPIPOLAR LINE (IN FIRST CAMERA VIEW) self_epipolar <- dltEpipolarLine(p=lm.matrix['occipital_condyle', ], cal.coeff1=cal.coeff, self=TRUE) ## CONFIRM THAT DISTANCE FROM ORIGINAL POINT TO SELF-EPIPOLAR LINE IS ZERO distancePointToLine(p=lm.matrix['occipital_condyle', ], l1=self_epipolar)
This function takes 3D coordinates and the DLT calibration coefficients corresponding to one camera view and returns the ideal pixel coordinates of the 3D points in that camera view.
dltInverse(cal.coeff, coor.3d)
dltInverse(cal.coeff, coor.3d)
cal.coeff |
a single column matrix of DLT calibration coefficients for one camera view. |
coor.3d |
a three-column matrix of 3D coordinates. |
When dltReconstruct
is used to reconstruct points in 3D based on pixel coordinates from two or more camera views, these 3D points can be projected back into any camera view at their "ideal" pixel coordinates (the "inverse" of reconstruction). The "ideal" pixel coordinates represent the pixel coordinates in each view if there were no error (i.e. all pixel coordinates in every view correspond to the exact same point in 3D). In any real-world system there is some error and these ideal pixel coordinates are compared to the original pixel coordinates used in the reconstruction to assess reconstruction error. dltInverse()
is called by dltCoefficients
and dltEpipolarLine
.
Since dltInverse()
only projects the 3D coordinates into a single camera view, only one column of the DLT coefficients should be input. Currently, dltInverse()
only works with the 11-parameter DLT model.
a two-column matrix of pixel coordinates of all points in coor.3d
in the camera view corresponding to cal.coeff
.
This function was modified by A Olsen from the Matlab function dlt_inverse()
written by T Hedrick.
Aaron Olsen
Abdel-Aziz, Y.I., Karara, H.M. (1971) Direct linear transformation into object space coordinates in close-range photogrammetry. Proc. Symp. on Close-Range Photogrammetry (University of Illinois at Urbana-Champaign).
Hedrick, T.L. (2008) Software techniques for two- and three-dimensional kinematic measurements of biological and biomimetic systems. Bioinspiration & Biomimetics, 3 (034001).
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltCalibrateCameras
,
dltReconstruct
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## READ LANDMARKS INTO MATRIX lm.matrix <- readLandmarksToMatrix(file=paste0(fdir, "lm_3d_a1.txt"), row.names=1) ## GET IDEAL 2D COORDINATES OF 3D POINTS IN FIRST CAMERA VIEW dltInverse(cal.coeff[, 1], lm.matrix) ## GET IDEAL 2D COORDINATES OF 3D POINTS IN SECOND CAMERA VIEW dltInverse(cal.coeff[, 2], lm.matrix)
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## READ LANDMARKS INTO MATRIX lm.matrix <- readLandmarksToMatrix(file=paste0(fdir, "lm_3d_a1.txt"), row.names=1) ## GET IDEAL 2D COORDINATES OF 3D POINTS IN FIRST CAMERA VIEW dltInverse(cal.coeff[, 1], lm.matrix) ## GET IDEAL 2D COORDINATES OF 3D POINTS IN SECOND CAMERA VIEW dltInverse(cal.coeff[, 2], lm.matrix)
This function uses DLT calibration coefficients to find corresponding points along a curve viewed from two different cameras in stereo camera setup.
dltMatchCurvePoints(lm.list, cal.coeff, min.direct.tangency = 25, min.fill.tangency = 0, epi.err.weight = 1, rec.err.weight = 0) ## S3 method for class 'dltMatchCurvePoints' summary(object, print.tab = '', ...)
dltMatchCurvePoints(lm.list, cal.coeff, min.direct.tangency = 25, min.fill.tangency = 0, epi.err.weight = 1, rec.err.weight = 0) ## S3 method for class 'dltMatchCurvePoints' summary(object, print.tab = '', ...)
lm.list |
a list of curve points from two camera views (see |
cal.coeff |
a two-column matrix of DLT calibration coefficients, where each column corresponds to the views from which points in |
min.direct.tangency |
Threshold (in degrees) to determine which points will be matched during initial (direct) matching. Regions of the curve that have a tangent less than this value relative to the epipolar line will be skipped. |
min.fill.tangency |
Threshold (in degrees) to determine which points will be matched during second step (fill) of matching. Regions of the curve that have a tangent less than this value relative to the epipolar line will be skipped. |
epi.err.weight |
Weight of epipolar error in determining matching points during second step of matching. This weight is taken relative to |
rec.err.weight |
Weight of reconstruction error in determining matching points during second step of matching. This weight is taken relative to |
object |
a list of class |
print.tab |
Tabs preceding lines printed to console. |
... |
Further arguments passed to or from other methods. |
Point reconstruction with a stereo camera setup requires pixel coordinates of the same point in two or more camera views. Curves can also be reconstructed in a stereo camera setup if reconstructed as a series of single points. This, however, poses an additional challenge: that of identifying the same point on a curve viewed from two different perspectives. A point half-way along the curve in one view will not necessarily correspond to a point half-way along the same curve in another view. dltMatchCurvePoints()
solves this challenge by using epipolar geometry informed by the DLT calibration coefficients (Yekutieli et al. 2007).
In a stereo camera setup, a point in one camera view must fall somewhere along a line in a second camera view. This line is called its epipolar line. If the same curve has been digitized in two camera views, the epipolar line of a point on the first curve should intersect the curve in the second camera view. The point at which the epipolar line intersects the curve in the second view represents the corresponding point on the second curve. dltMatchCurvePoints()
iterates through all the points on one curve and uses epipolar geometry to identify the corresponding point on a second curve. The corresponding point is identified as a point on the epipolar line that is closest to the curve in the second view (rather than finding the intersection, per se). For more details on the use of epipolar geometry to solve for corresponding points see Yekutieli et al. (2007).
Two different types of curve point input to dltMatchCurvePoints()
are possible. The first type is a list with two elements (list[[1]]
and list[[2]]
), containing the curve points of the first and second camera view, respectively. The second type is a list of the same form as the landmark list described in readLandmarksToList
. The main elements of the landmark list are the landmarks and curves (list[['landmark1']]
, list[['curve1']]
, etc.). Each main element then has two elements (e.g. list[['curve1']][[1]]
, list[['curve1']][[2]]
) corresponding to the first and second camera views, respectively. The curve points themselves should be densely sampled pixel coordinates (e.g. single pixel spacing) in order to improve matching accuracy.
dltMatchCurvePoints()
returns the landmark list as the element match.lm.list
in the same format as the input, except that all curve points will be corresponding points. Note that list input is used, rather than a matrix, because the number of curve points may differ between the two views. Once the corresponding curve points are identified, however, the number of curve points in each view will be equal. Landmarks and curves containing less than three points are ignored and returned just as input. In this way, all landmarks and curve points can be passed through dltMatchCurvePoints()
without having to be processed separately.
Although a stereo camera system may consist of more than two cameras, the coefficients of only two cameras should be input to dltMatchCurvePoints()
. Only the coefficients of the two camera views for which corresponding curve points are being identified are relevant. Currently, this function can only match curve points between two camera views using the 11-parameter DLT model.
The curve points chosen as the reference are used to generate epipolar lines in a second camera view. The results will differ slightly depending on which view is chosen as a reference. By default, dltMatchCurvePoints()
uses the curve with the maximum number of points as a reference. Users can specify which view is to be used as reference through ref.view
. Setting ref.view
to "min" will use the curve with the minimum number of points as a reference. Setting ref.view
to 1 or 2 will use the first view or second view as a reference, respectively.
As dltMatchCurvePoints()
steps through each point on the reference curve, it searches for the closest point on the epipolar line to the second curve. Rather than search for the closest point among all of the second curve points, dltMatchCurvePoints()
only searches over a sliding window of points. window.size
is the number of curve points considered at each iteration in identifying the corresponding point. A lower window.size
will decrease the run-time but will potentially cause the function to miss corresponding points. If curve.pt.dist
values are low, the current window.size
is probably appropriate. window.size
can be increased if curve.pt.dist
values are high (over several pixels).
When the epipolar line is nearly parallel to the curve in the non-reference view, several points are equally likely to be the corresponding point and determining the actual corresponding point is impossible without additional information. The angle between the epipolar line and the points on the non-reference curve is referred to here as the tangency angle. When the tangency angle for points on the non-reference curve is less than min.tangency.angle
, the current reference point is skipped. Additionally, when the points near a point on the non-reference curve are also very close to the epipolar line, a wrong match is more likely. Within the window of candidate points, the distance from each point to the epipolar line is calculated. The slope of these distances away from the point closest to the epipolar line (the minimum distance) is referred to here as the adjacent point distance slope. When the adjacent point distance slope is lower, the confidence that the minimum distance point is the correct match decreases. When this adjacent point distance slope is less than min.dist.adj.slope
, the current reference point is skipped. The min.tangency.angle
and min.dist.adj.slope
are similar criteria, however the min.dist.adj.slope
might provide more robust results with more irregular curves. Users might need to increase one or both of these values to obtain satisfactory results.
When reference points are skipped, these are filled in at the end with straight lines extending between defined points to either side of the skipped regions. Straight lines are used because these regions are likely to be nearly linear, owing to their minimal deviation from the epipolar line.
In addition to returning match.lm.list
, dltMatchCurvePoints()
also returns two vectors (or lists of vectors, depending on the format of lm.list
) that can be used to assess the accuracy of the curve point matching. epipolar.dist
is the epipolar distance between the epipolar line of the reference point and the corresponding point in the non-reference view. The first and last point are assumed to correspond, so there will be some epipolar error for these points. The remaining points are chosen on the epipolar line of the reference point, so their epipolar error will be zero. Future implementations may allow users to specify that corresponding points be on the curve in the second view and not necessarily on the epipolar line, in which case epipolar.dist
will become more relevant. curve.pt.dist
is the distance between the epipolar line and the nearest point on the curve in the second view for each curve point. If the exact same curve has been digitized in the two views, curve.pt.dist
should be low (within a pixel or less).
a list of class "dltMatchCurvePoints"
with the following elements:
match.lm.list |
a landmark list of matched curve points (and landmarks if also input). |
epipolar.dist |
a list or vector of the epipolar distance between the epipolar line of the reference points and the corresponding non-reference point. In current implementation, all values will be zero except the start and end points. |
curve.pt.dist |
a list or vector of the distances from the chosen corresponding points and the nearest point on the non-reference curve. |
This function was written by A Olsen based on the methodology described in Yekutieli et al. 2007.
Aaron Olsen
Yekutieli, Y., Mitelman, R., Hochner, B. and Flash, T. (2007). Analyzing Octopus Movements Using Three-Dimensional Reconstruction. Journal of Neurophysiology, 98, 1775–1790.
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
readLandmarksToList
,
dltEpipolarLine
,
dltEpipolarDistance
,
dltNearestPointOnEpipolar
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO LANDMARK DATA file <- paste0(fdir, "lm_2d_a2_v", 1:2, ".txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=paste0(fdir, "cal_coeffs.txt"))) ## READ LANDMARKS INTO LIST lm.list <- readLandmarksToList(file=file, row.names=1) ## MATCH CURVE POINTS FOR ONE CURVE ## FIRST TYPE OF LANDMARK INPUT ## RETURNS LIST OF MATCHING POINTS WITHOUT CURVE NAME dlt_match <- dltMatchCurvePoints(lm.list$pterygoid_crest_R, cal.coeff) ## PRINT SUMMARY summary(dlt_match) ## SET A DIFFERENT REFERENCE VIEW ## SECOND VIEW HAS 80 FEWER POINTS ## dlt_match <- dltMatchCurvePoints(lm.list$pterygoid_crest_R, cal.coeff, ref.view=2) ## MATCH CURVE POINTS FOR ALL CURVES IN LIST ## SECOND TYPE OF LANDMARK INPUT ## RETURNS LIST OF ALL LANDMARKS AND MATCHED CURVE POINTS WITH CURVE NAMES ## dlt_match <- dltMatchCurvePoints(lm.list, cal.coeff)
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO LANDMARK DATA file <- paste0(fdir, "lm_2d_a2_v", 1:2, ".txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=paste0(fdir, "cal_coeffs.txt"))) ## READ LANDMARKS INTO LIST lm.list <- readLandmarksToList(file=file, row.names=1) ## MATCH CURVE POINTS FOR ONE CURVE ## FIRST TYPE OF LANDMARK INPUT ## RETURNS LIST OF MATCHING POINTS WITHOUT CURVE NAME dlt_match <- dltMatchCurvePoints(lm.list$pterygoid_crest_R, cal.coeff) ## PRINT SUMMARY summary(dlt_match) ## SET A DIFFERENT REFERENCE VIEW ## SECOND VIEW HAS 80 FEWER POINTS ## dlt_match <- dltMatchCurvePoints(lm.list$pterygoid_crest_R, cal.coeff, ref.view=2) ## MATCH CURVE POINTS FOR ALL CURVES IN LIST ## SECOND TYPE OF LANDMARK INPUT ## RETURNS LIST OF ALL LANDMARKS AND MATCHED CURVE POINTS WITH CURVE NAMES ## dlt_match <- dltMatchCurvePoints(lm.list, cal.coeff)
Given the same point in two camera views, this function finds the nearest point on the epipolar line of the point in the first view to a point or points in the second view.
dltNearestPointOnEpipolar(p1, p2, cal.coeff)
dltNearestPointOnEpipolar(p1, p2, cal.coeff)
p1 |
vector of x,y pixel coordinates for a point in the camera view corresponding to the first column of |
p2 |
an x,y vector or two-column matrix of a point or points in a second camera view, corresponding to the second column of |
cal.coeff |
a two-column matrix of DLT calibration coefficients, where each column corresponds to the views from which |
In a stereo camera setup, a point in one camera view must fall somewhere along a line in a second camera view. This line is called its epipolar line. Due to error in manually selecting the same point in two camera views and error in the calibration, the epipolar line of the point in the first view will not intersect exactly with the same point in the second view. The nearest point on the epipolar line is a point at a minimum distance from the point in the second view. This is equivalent to the orthogonal projection (orthogonalProjectionToLine
) of the point in the second view onto the epipolar line of the point in the first view. The length of this line is the epipolar distance (dltEpipolarDistance
).
dltNearestPointOnEpipolar()
first finds the epipolar line of p1
, a point in the first camera view, and then finds the point on this epipolar line nearest to point(s) p2
in the second camera view. If p2
is a single point, dltNearestPointOnEpipolar()
finds the point on the epipolar line closest to p2
. If p2
is a matrix of points, the point in p2
closest to the epipolar line is first identified and then the point on the epipolar line closest to this point is determined.
a list with the following elements:
matching.pt |
an x,y vector of the point on the epipolar line of |
min.idx |
the index in |
p2.dist |
the epipolar distance between |
Aaron Olsen
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltCalibrateCameras
, dltEpipolarDistance
, dltEpipolarLine
, dltMatchCurvePoints
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_files <- paste0(fdir, c("lm_2d_a1_v1.txt", "lm_2d_a1_v2.txt")) ## READ LANDMARKS INTO MATRIX lm.array <- readLandmarksToArray(file=lm_files, row.names=1) ## FIND THE NEAREST POINT ON THE EPIPOLAR LINE OF P1 dltNearestPointOnEpipolar(p1=lm.array[3, , 1], p2=lm.array[3, , 2], cal.coeff=cal.coeff) ## FIND THE NEAREST POINT ON THE EPIPOLAR LINE OF P1 ## THIS TIME USING ALL LANDMARKS IN THAT VIEW ## FUNCTION IDENTIFIES THE CORRECT LANDMARK IN THE SECOND VIEW AS THE SAME LANDMARK dltNearestPointOnEpipolar(p1=lm.array[3, , 1], p2=lm.array[, , 2], cal.coeff=cal.coeff)
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_files <- paste0(fdir, c("lm_2d_a1_v1.txt", "lm_2d_a1_v2.txt")) ## READ LANDMARKS INTO MATRIX lm.array <- readLandmarksToArray(file=lm_files, row.names=1) ## FIND THE NEAREST POINT ON THE EPIPOLAR LINE OF P1 dltNearestPointOnEpipolar(p1=lm.array[3, , 1], p2=lm.array[3, , 2], cal.coeff=cal.coeff) ## FIND THE NEAREST POINT ON THE EPIPOLAR LINE OF P1 ## THIS TIME USING ALL LANDMARKS IN THAT VIEW ## FUNCTION IDENTIFIES THE CORRECT LANDMARK IN THE SECOND VIEW AS THE SAME LANDMARK dltNearestPointOnEpipolar(p1=lm.array[3, , 1], p2=lm.array[, , 2], cal.coeff=cal.coeff)
This function takes 2D pixel coordinates of a point or points from two more camera views and uses DLT coefficients to reconstruct their position in 3D.
dltReconstruct(cal.coeff, coor.2d, min.views = 2) ## S3 method for class 'dltReconstruct' summary(object, ...)
dltReconstruct(cal.coeff, coor.2d, min.views = 2) ## S3 method for class 'dltReconstruct' summary(object, ...)
cal.coeff |
a matrix of DLT calibration coefficients. The columns correspond to each camera view and the column order should match the camera view order of the landmarks in |
coor.2d |
2D pixel coordinates from two or more camera views. Format can be either a landmark matrix, list or array. |
min.views |
the minimum number of views required for a point to be reconstructed in 3D. |
object |
a list of class |
... |
further arguments passed to or from other methods. |
This function uses DLT coefficients (calculated using dltCalibrateCameras
, for example) to reconstruct the 3D position of points, based on their 2D position in two or more camera views. 2D pixel coordinates can be input as a landmark matrix (readLandmarksToMatrix
), as a list (readLandmarksToList
) or as an array (readLandmarksToArray
).
A minimum of two views is required for 3D reconstruction although additional camera views can be used, potentially improving reconstruction accuracy. Points that are present in fewer views than specified by min.views
will be assigned NA values in the returned 3D matrix (coor.3d
).
After 3D reconstruction, dltReconstruct()
performs the inverse operation, taking the reconstructed, 3D coordinates and solving for the 2D position of the points in each camera view. These inverse 2D coordinates are compared with the original coordinates and their difference is returned as the root-mean-square (RMS) reconstruction error (list$rmse
). This error is similar to the epipolar distance (dltEpipolarDistance
). The summary()
function can be used to view the error by landmark.
Currently, dltReconstruct()
only works with the 11-parameter DLT model.
a list of class "dltReconstruct"
with the following elements:
coor.3d |
a 2D or 3D landmark matrix. |
rmse |
the root-mean-square reconstruction error (in pixels). |
This function was modified by A Olsen from the Matlab function dlt_reconstruct()
written by T Hedrick.
Aaron Olsen
Abdel-Aziz, Y.I., Karara, H.M. (1971) Direct linear transformation into object space coordinates in close-range photogrammetry. Proc. Symp. on Close-Range Photogrammetry (University of Illinois at Urbana-Champaign).
Hedrick, T.L. (2008) Software techniques for two- and three-dimensional kinematic measurements of biological and biomimetic systems. Bioinspiration & Biomimetics, 3 (034001).
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltCalibrateCameras
,
readLandmarksToMatrix
,
readLandmarksToList
,
readLandmarksToArray
,
dltEpipolarDistance
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_files <- paste0(fdir, c("lm_2d_a1_v1.txt", "lm_2d_a1_v2.txt")) ## READ LANDMARKS INTO MATRIX lm.matrix <- readLandmarksToMatrix(file=lm_files, row.names=1) ## RECONSTRUCT LANDMARKS IN 3D (MATRIX INPUT) dlt_recon <- dltReconstruct(cal.coeff=cal.coeff, coor.2d=lm.matrix) ## OTHER POSSIBLE LANDMARK FORMAT INPUTS ## ## READ LANDMARKS INTO LIST lm.list <- readLandmarksToList(file=lm_files, row.names=1) ## RECONSTRUCT LANDMARKS IN 3D (LIST INPUT) dlt_recon <- dltReconstruct(cal.coeff=cal.coeff, coor.2d=lm.list) ## READ LANDMARKS INTO ARRAY lm.array <- readLandmarksToArray(file=lm_files, row.names=1) ## RECONSTRUCT LANDMARKS IN 3D (ARRAY INPUT) dlt_recon <- dltReconstruct(cal.coeff=cal.coeff, coor.2d=lm.array)
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CALIBRATION COEFFICIENTS IN TWO CAMERA STEREO SETUP cc_file <- paste0(fdir, "cal_coeffs.txt") ## LOAD COEFFICIENTS cal.coeff <- as.matrix(read.table(file=cc_file)) ## GET LANDMARKS IN FIRST CAMERA VIEW lm_files <- paste0(fdir, c("lm_2d_a1_v1.txt", "lm_2d_a1_v2.txt")) ## READ LANDMARKS INTO MATRIX lm.matrix <- readLandmarksToMatrix(file=lm_files, row.names=1) ## RECONSTRUCT LANDMARKS IN 3D (MATRIX INPUT) dlt_recon <- dltReconstruct(cal.coeff=cal.coeff, coor.2d=lm.matrix) ## OTHER POSSIBLE LANDMARK FORMAT INPUTS ## ## READ LANDMARKS INTO LIST lm.list <- readLandmarksToList(file=lm_files, row.names=1) ## RECONSTRUCT LANDMARKS IN 3D (LIST INPUT) dlt_recon <- dltReconstruct(cal.coeff=cal.coeff, coor.2d=lm.list) ## READ LANDMARKS INTO ARRAY lm.array <- readLandmarksToArray(file=lm_files, row.names=1) ## RECONSTRUCT LANDMARKS IN 3D (ARRAY INPUT) dlt_recon <- dltReconstruct(cal.coeff=cal.coeff, coor.2d=lm.array)
This function uses a set of grid points, ideally other than those used in stereo camera calibration, to test calibration accuracy. Results of both distance-based and position-based accuracy tests are returned.
dltTestCalibration(cal.coeff, coor.2d, nx, sq.size, reciprocal = TRUE, align.princomp = FALSE) ## S3 method for class 'dltTestCalibration' summary(object, print.tab = '', ...)
dltTestCalibration(cal.coeff, coor.2d, nx, sq.size, reciprocal = TRUE, align.princomp = FALSE) ## S3 method for class 'dltTestCalibration' summary(object, print.tab = '', ...)
cal.coeff |
a matrix of DLT calibration coefficients. The columns correspond to each camera view and the column order should match the camera view order in the fourth dimension of the |
coor.2d |
a four-dimensional array of grid points. The first two dimensions correspond to each matrix of grid points, the third corresponds to each grid position/orientation and the fourth corresponds to each camera view. These can be read in from file by |
nx |
the number of points along the first dimension (e.g. this would be the number of points in each row if points in |
sq.size |
the size of the grid squares in real-world units (e.g. millimeters). |
reciprocal |
a logical indicating whether epipolar distance should be calculated reciprocally and then averaged. |
align.princomp |
a logical indicating whether checkerboard corners should be aligned along principal coordinate axes prior to error testing (serves to describe error along axes that may be more physically meaningful than the initial, arbitrary coordinate system). |
object |
a list of class |
print.tab |
Tabs preceding lines printed to console. |
... |
further arguments passed to or from other methods. |
Although the RMS errors reported by dltCalibrateCameras
can be used to assess the accuracy of a stereo camera setup, these represent how well the DLT parameters fit the calibration point set and not the reconstruction accuracy per se. It has been argued that in order to obtain a true estimation of reconstruction accuracy, an independent assessment criterion is required (Challis & Kerwin 1992). With the StereoMorph package, this is best accomplished by photographing a grid not used in the calibration and of a different square size (to test for proper scaling). These images are taken and the internal corners extracted just as in the calibration step (see dltCalibrateCameras
), again ensuring that the test images fully sample the calibration volume and that the extracted point orders correspond between the two views. The input format of coor.2d
to dltTestCalibration()
is the same format as the coor.2d
input to dltCalibrateCameras
.
dltTestCalibration()
measures the calibration accuracy using two approaches: a distance-based approach and a position-based approach. For the distance-based approach (e.g. Tashman & Anderst 2003; Brainerd et al. 2010), random pairs of grid points are chosen (without resampling), reconstructed and the distance between the reconstructed points is compared with the actual distance. The deviations from the true distance (interpoint distance error or IPD error) for each pair of points are returned in the ipd.error
vector. dltTestCalibration()
also measures IPD error of only adjacent points, returned in the vector adj.pair.ipd.error
. With a sufficient number of grid points, adjacent points are close enough that one can test how IPD error varies as a function of the distance from the approximate center of the calibrated volume (adj.pair.centroid.dist
) or along a particular dimension (adj.pair.mean.pos
).
One challenge in interpreting the IPD error, however, is that each deviation represents error in the x, y and z position of two points. This makes it difficult to assess the accuracy of a particular point or along a particular dimension. Since we do not know the 3D coordinates of a test grid placed at an arbitrary orientation in the calibration volume, we must find the best fit 3D position in order to assess positional accuracy. For the position-based approach, dltTestCalibration()
takes an ideal grid of the same square size and dimensions and optimally aligns it with the reconstructed test points using findOptimalPointAlignment
. The reconstructed test points can then be compared with their corresponding reference points. These errors are returned in the matrix aitr.error
(aligned ideal to reconstructed point position). This approach has the disadvantage that best fit alignment will tend to align the reference grid where the error is highest so as to minimize differences. This can decrease error where it is in actuality relatively high and vice versa.
a list of class "dltTestCalibration"
with the following elements:
num.grids |
the number of test calibration grids used in accuracy assessment. |
epipolar.error |
the epipolar error (distance) for every test calibration point. This is the reciprocal epipolar distance if |
epipolar.rmse |
the root-mean-square error of |
ipd.error |
a vector of the deviations from the true distance between random pairs of points (without resampling). |
pair.dist |
a vector of the true distances between the random pairs of points in |
ipd.rmse |
the root-mean-square error of |
adj.pair.ipd.error |
a vector of the deviations from the true distance between random pairs of adjacent points (without resampling). |
adj.pair.mean.pos |
a three-column matrix of the mean position (midpoint) of the adjacent pairs of points in |
adj.pair.centroid.dist |
a vector of the distances from each point in |
aitr.error |
a three-column matrix of the x, y and z position errors for the reconstructed test calibration points relative to optimally aligned ideal grid points. |
aitr.dist.error |
a vector of the distances between the reconstructed test calibration points and the optimally aligned ideal grid points. Note that ideally this distance should be zero so all values in this vector are positive. |
aitr.dist.rmse |
the RMS error (or deviation) of |
aitr.rmse |
a vector of the RMS error (or deviation) of |
aitr.pos |
a three-column matrix of the ideal grid points after best fit alignment to the reconstructed grid points. |
aitr.centroid.dist |
a vector of the distances between each AITR point and the centroid of all AITR points. |
Aaron Olsen
Challis, J.H. and Kerwin, D.G. (1992). Accuracy assessment and control point configuration when using the DLT for photogrammetry. Journal of Biomechanics, 25 (9), 1053–1058.
Tashman, S. and Anderst, W. (2003). In Vivo Measurement of Dynamic Joint Motion Using High Speed Biplane Radiography and CT: Application to Canine ACL Deficiency. Transactions of the ASME, 125, 238–245.
Brainerd, E.L., Baier, D.B., Gatesy, S.M., Hedrick, T.L., Metzger, K.A., Gilbert, S.L and Crisco, J.J. (2010). X-ray reconstruction of moving morphology (XROMM): Precision, accuracy and applications in comparative biomechanics research. Journal of Experimental Zoology, 313A, 262–279.
For a general overview of DLT: http://kwon3d.com/theory/dlt/dlt.html
dltCalibrateCameras
,
dltCoefficients
,
readCheckerboardsToArray
,
dltEpipolarDistance
,
findCheckerboardCorners
## SET NUMBER OF INTERNAL ROWS AND COLUMNS nx <- 21 ny <- 14 ## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CHECKERBOARD CORNERS FROM TEST CALIBRATION IMAGE SET file <- matrix(c(paste0(fdir, "test_cal_a", 1:11, "_v1.txt"), paste0(fdir, "test_cal_a", 1:11, "_v2.txt")), ncol=2) ## READ IN CHECKERBOARD CORNERS coor.2d <- readCheckerboardsToArray(file=file, nx=nx, ny=ny, col.reverse=FALSE) ## SET GRID SIZE OF TEST CHECKERBOARDS (IN MM) sq.size <- 4.2218 ## LOAD CALIBRATION COEFFICIENTS cal.coeff <- as.matrix(read.table(file=paste0(fdir, "cal_coeffs.txt"))) ## TEST CALIBRATION ACCURACY ## USE ONLY A SUBSET (FIVE) OF TEST CALIBRATION IMAGES ## IN THE TUTORIAL POINTS, UNITS NOT IN PIXELS ARE MILLIMETERS dlt_test <- dltTestCalibration(cal.coeff=cal.coeff, coor.2d=coor.2d[, , 1:5, ], nx=nx, sq.size=sq.size) ## RUN TEST ON ALL TEST CALIBRATION IMAGES ## Not run: dlt_test <- dltTestCalibration(cal.coeff=cal.coeff, coor.2d=coor.2d, nx=nx, sq.size=sq.size) ## End(Not run) ## PRINT SUMMARY summary(dlt_test) ## PLOT A HISTOGRAM OF THE INTERPOINT DISTANCE ERROR hist(dlt_test$ipd.error) ## PLOT ADJACENT POINT DISTANCE ERROR AS A FUNCTION OF POSITION ALONG THE Y-AXIS dev.new() plot(dlt_test$adj.pair.ipd.error, abs(dlt_test$adj.pair.mean.pos[, 2, ])) ## PLOT POSITION-BASED ERROR AS A FUNCTION OF POSITION ALONG THE X-AXIS dev.new() plot(dlt_test$aitr.pos[, 1, ], abs(dlt_test$aitr.error[, 1, ]))
## SET NUMBER OF INTERNAL ROWS AND COLUMNS nx <- 21 ny <- 14 ## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CHECKERBOARD CORNERS FROM TEST CALIBRATION IMAGE SET file <- matrix(c(paste0(fdir, "test_cal_a", 1:11, "_v1.txt"), paste0(fdir, "test_cal_a", 1:11, "_v2.txt")), ncol=2) ## READ IN CHECKERBOARD CORNERS coor.2d <- readCheckerboardsToArray(file=file, nx=nx, ny=ny, col.reverse=FALSE) ## SET GRID SIZE OF TEST CHECKERBOARDS (IN MM) sq.size <- 4.2218 ## LOAD CALIBRATION COEFFICIENTS cal.coeff <- as.matrix(read.table(file=paste0(fdir, "cal_coeffs.txt"))) ## TEST CALIBRATION ACCURACY ## USE ONLY A SUBSET (FIVE) OF TEST CALIBRATION IMAGES ## IN THE TUTORIAL POINTS, UNITS NOT IN PIXELS ARE MILLIMETERS dlt_test <- dltTestCalibration(cal.coeff=cal.coeff, coor.2d=coor.2d[, , 1:5, ], nx=nx, sq.size=sq.size) ## RUN TEST ON ALL TEST CALIBRATION IMAGES ## Not run: dlt_test <- dltTestCalibration(cal.coeff=cal.coeff, coor.2d=coor.2d, nx=nx, sq.size=sq.size) ## End(Not run) ## PRINT SUMMARY summary(dlt_test) ## PLOT A HISTOGRAM OF THE INTERPOINT DISTANCE ERROR hist(dlt_test$ipd.error) ## PLOT ADJACENT POINT DISTANCE ERROR AS A FUNCTION OF POSITION ALONG THE Y-AXIS dev.new() plot(dlt_test$adj.pair.ipd.error, abs(dlt_test$adj.pair.mean.pos[, 2, ])) ## PLOT POSITION-BASED ERROR AS A FUNCTION OF POSITION ALONG THE X-AXIS dev.new() plot(dlt_test$aitr.pos[, 1, ], abs(dlt_test$aitr.error[, 1, ]))
Returns the RMS error from dltCoefficients
after applying a given set of transformation parameters to grid points in stereo camera calibration. This function is called internally by the function dltCalibrateCameras
to estimate the position and orientation of a set of calibration grid points that minimizes calibration error.
dltTransformationParameterRMSError(p, coor.2d, nx, ny, sx, sy = NULL, p.fixed = NULL)
dltTransformationParameterRMSError(p, coor.2d, nx, ny, sx, sy = NULL, p.fixed = NULL)
p |
a vector of six transformation parameters per grid. The first three being rotational parameters (rotation about the z, y and x axes, respectively) and the second three being translational parameters (translation along the x, y and z axes, respectively). For more than one grid, these six values are concatenated as a vector. |
coor.2d |
a four-dimensional array of grid points passed from |
nx |
the number of points along the first dimension (e.g. this would be the number of points in each row if points are listed first by row). |
ny |
the number of points along the second dimension (e.g. this would be the number of points in each column if points are listed first by row). |
sx |
a scaling factor along the first dimension. |
sy |
a scaling factor along the second dimension. If the grid blocks are squares, this can be left as |
p.fixed |
a set of transformation parameters to be appended to the beginning of |
the mean RMS error from dltCoefficients
across all views.
Aaron Olsen
transformPlanarCalibrationCoordinates
,
dltCoefficients
,
dltCalibrateCameras
Creates a checkerboard image of specified dimensions and saves to an input file path. The dimensions of the checkerboard are specified by the number of internal corners (the number of squares minus one).
drawCheckerboard(nx, ny, square.size, file = NULL, margin.x = c(round(square.size/2), round(square.size/2)), margin.y = c(round(square.size/2), round(square.size/2)), filename = NULL, ...)
drawCheckerboard(nx, ny, square.size, file = NULL, margin.x = c(round(square.size/2), round(square.size/2)), margin.y = c(round(square.size/2), round(square.size/2)), filename = NULL, ...)
nx |
the number of internal corners in the horizontal direction (the number of squares in each row minus one). |
ny |
the number of internal corners in the vertical direction (the number of squares in each column minus one). |
square.size |
the square size in pixels. |
file |
the file path and name to which the image should be saved. The filename must be a valid image filename. Acceptable extensions are: jpg, jpeg, bmp, png and tiff. |
margin.x |
the margin in pixels on the left and right sides of the checkerboard pattern. |
margin.y |
the margin in pixels on the top and bottom of the checkerboard pattern. |
filename |
Duplicate with |
... |
further arguments to be passed to the image function corresponding to the extension in |
This function requires the grid
package. The image type is determined automatically from the filename and the corresponding image writing function is called.
returns null device
Aaron Olsen
## NUMBER OF INTERNAL CORNERS IN THE HORIZONTAL DIMENSION ## NUMBER OF ROWS OF SQUARES MINUS ONE nx <- 21 ## NUMBER OF INTERNAL CORNERS IN THE VERTICAL DIMENSION ## NUMBER OF COLUMNS OF SQUARES MINUS ONE ny <- 14 ## SQUARE SIZE IN PIXELS square.size <- 200 ## WHERE TO SAVE THE FILE filename <- paste0("checkerboard_", nx, "_", ny, "_", square.size, ".jpeg") ## Not run: ## DRAW CHECKERBOARD ## FILE WILL BE CREATED IN CURRENT WORKING DIRECTORY drawCheckerboard(nx=nx, ny=ny, square.size=square.size, filename=filename) ## End(Not run)
## NUMBER OF INTERNAL CORNERS IN THE HORIZONTAL DIMENSION ## NUMBER OF ROWS OF SQUARES MINUS ONE nx <- 21 ## NUMBER OF INTERNAL CORNERS IN THE VERTICAL DIMENSION ## NUMBER OF COLUMNS OF SQUARES MINUS ONE ny <- 14 ## SQUARE SIZE IN PIXELS square.size <- 200 ## WHERE TO SAVE THE FILE filename <- paste0("checkerboard_", nx, "_", ny, "_", square.size, ".jpeg") ## Not run: ## DRAW CHECKERBOARD ## FILE WILL BE CREATED IN CURRENT WORKING DIRECTORY drawCheckerboard(nx=nx, ny=ny, square.size=square.size, filename=filename) ## End(Not run)
Extracts frames from a video saving them as a series of images
extractFrames(file = NULL, save.to = NULL, frames = NULL, names = NULL, ext = 'jpeg', qscale = 2, frame.start = 0, video.i = NULL, warn.min = 100)
extractFrames(file = NULL, save.to = NULL, frames = NULL, names = NULL, ext = 'jpeg', qscale = 2, frame.start = 0, video.i = NULL, warn.min = 100)
file |
Video file from which frames are to be extracted. |
save.to |
Where to save the extracted frames. |
frames |
The frames to be extracted, starting with 0. |
names |
Names to be given to the extracted frames. If |
ext |
The image type/extension to be added to each extract frame. |
qscale |
Integer indicating the image quality of the extracted frames. This is an input parameter passed direclty to ffmpeg. |
frame.start |
The time (in msec) corresponding to the frame immediately before the first frame change. |
video.i |
Video metadata passed to the function. This parameter is only intended for internal use. |
warn.min |
The minimum number of extracted frames for which the user is prompted and has to respond 'y' prior to frame extraction. This is intended to prevent the user from mistakenly extracting thousands of frames. |
In order to use this function you must separately install the ffmpeg video codec library. For instructions please refer to the 'Extracting video frames' section of the most recent StereoMorph user guide here. This function can be used interactively with prompts by calling extractFrames()
. The user will then be prompted for all necessary input parameters. This is useful because the function will report the number of frames in the video before prompting which frames the user would like to extract.
NULL
Aaron Olsen
## Not run: # Use extractFrames() with interactive prompts extractFrames() # Extract the first 20 frames from a video extractFrames(file='Example_video.mov', save.to='Frames', frames=0:20) ## End(Not run)
## Not run: # Use extractFrames() with interactive prompts extractFrames() # Extract the first 20 frames from a video extractFrames(file='Example_video.mov', save.to='Frames', frames=0:20) ## End(Not run)
This function finds the internal corners of a checkerboard pattern in an image.
findCheckerboardCorners(image.file, nx, ny, corner.file=NULL, verify.file=NULL, perim.min = 'auto', perim.max = 'auto', dilations.min = 0, dilations.max = 7, sub.pix.win = NULL, sub.pix.win.min = NULL, quad.fit.max=4, poly.cont.min=-0.3, poly.cont.max=0.3, quad.approx.thresh = 'auto', flip = FALSE, print.progress=TRUE, verbose=FALSE, debug = FALSE)
findCheckerboardCorners(image.file, nx, ny, corner.file=NULL, verify.file=NULL, perim.min = 'auto', perim.max = 'auto', dilations.min = 0, dilations.max = 7, sub.pix.win = NULL, sub.pix.win.min = NULL, quad.fit.max=4, poly.cont.min=-0.3, poly.cont.max=0.3, quad.approx.thresh = 'auto', flip = FALSE, print.progress=TRUE, verbose=FALSE, debug = FALSE)
image.file |
File path(s) to image(s) or to folder(s) containing image(s) (and only images). The image(s) should be a JPEG and include a checkerboard pattern. Can be a vector or matrix. Many different inputs accepted, see "Examples". |
nx |
The number of internal corners in the checkerboard along one dimension. Note that this is not the number of squares (see "Details"). |
ny |
The number of internal corners in the checkerboard along a second dimension. |
corner.file |
File path(s) to text file(s) or to folder(s) where the corners should be saved. Can be a vector or matrix. If |
verify.file |
File path(s) to JPEG image(s) or to folder(s) where verification images should be saved. Can be a vector or matrix. If |
perim.min |
The minimum expected perimeter of a black square in the checkerboard pattern (in pixels). |
perim.max |
The maximum expected perimeter of a black square in the checkerboard pattern (in pixels). |
dilations.min |
The initial number of dilations to perform on the image. See "Details". |
dilations.max |
The maximum number of dilations to perform on the image. If equal to |
sub.pix.win |
The window size to use in determining the corner positions to subpixel resolution. If |
sub.pix.win.min |
Only relevant if sub.pix.win is |
quad.fit.max |
Fit threshold used to identify quadrangles. |
poly.cont.min |
The minimum allowed aspect ratio of the polygon contours, used as a threshold in identifying quadrangles. |
poly.cont.max |
The maximum allowed aspect ratio of the polygon contours, used as a threshold in identifying quadrangles. |
quad.approx.thresh |
A threshold for the perimeter of black squares in which method to use to approximate the shape as a quadrangle. |
flip |
Logical whether the order of the corners should be flipped. |
print.progress |
Logical indicating whether the function progress should be printed to the console. See |
verbose |
Logical indicating whether more detailed progress reports to the console. If |
debug |
Logical indicating whether images should be created at each of several steps in the corner search. These will be written to the same location as the images written to |
This function automatically detects checkerboard corners in an image and returns the pixel coordinates of the internal corners (where the corners of the black squares contact other black squares) to subpixel resolution. The function uses several C++ functions for image processing written by the author and compiled with the StereoMorph package but hidden until documentation can be written for more general use. Currently the function only works with JPEG images (.jpg or .jpeg); this is the most common digital camera image format output. For large images (10-20 MB), the function can take from 5-15 seconds per image.
image.file
input to the function can be of several different forms. First, it can be file paths to particular images or file paths to a folder or folders containing images. Secondly, it can be in a vector or matrix format. The format of image.file
will dictate the structure of the value returned by the function. If a single image file is input, a two-colum matrix of corners (where the two columns correspond to the x, y pixel coordinates) is returned. If the input is a vector of file paths or folders containing images, a three-dimensional array is returned; the first two dimensions are the rows and columns of each corner matrix and the third dimension is the order of the corresponding image files in image.file
. If the input is a matrix of file paths or folders containing images, a four-dimensional array is returned; the first two dimensions are the rows and columns of each corner matrix and the third and fourth dimensions are the positions of the corresponding image files in the image.file
matrix. If image.file
is a folder or folders containing images, the folders cannot contain any other files.
The inputs corner.file
and verify.file
are optional but if they are non-NULL
, they should be of the same format as image.file
. If image.file
is a folder or folders containing images, folders can also be input for corner.file
and verify.file
. In this case, the function will automatically name the corner files and verify image files with the same names as the images and as text files and JPEG files, respectively. The corners are saved to a text file as a two column matrix without a header or row names.
For every input image, the function begins by reading in the image (using readJPEG()
of the 'jpeg' package). For large images this is one of the most time-consuming steps. The image is converted to grayscale using the internal function rgbToGray()
. The image is thresholded to create a binary image (black and white) based on an adaptive threshold. The threshold is created using the internal function meanBlurImage()
and the image thresholded with the internal function thresholdImageMatrix()
. Morphological closing is performed to reduce noise using the internal functions dilateImage()
and erodeImage()
.
The function then proceeds to dilate the image (expand white areas and consolidate black areas) using a 3x3 square kernel for the range specified by dilations.min
and dilations.max
. This separates the black squares from each other so that their perimeters can be detected as separate contours. For each dilation, all edge points are identified (black pixels with a neighboring white pixel and vice versa) using the internal function findBoundaryPoints()
. Contours (connected edge points) are identified by the internal function generateQuads()
, retaining only contours that are quadrangles. The midpoints between adjoining corners of all the quads are found using the internal function intCornersFromQuads()
; among these will be the full set of internal corners.
If the initial set of internal corners exceeds the expectation, the internal corners are filtered, fitting a line to the internal corner set and removing the points at the furthest difference from the line of best fit until the number of corners matches the expectation. The filtered internal corner set is then ordered using the internal function orderCorners()
so that first corner is the top left most corner in the pattern and the sequence of internal corners proceeds along nx
first and ny
second. Lastly, the function finds the internal corner positions to subpixel resolution (using the internal function findCornerSubPix()
) by sampling a window around the approximate location of the internal corners (of dimensions determined by sub.pix.win
) to find a point optimally positioned at the intersection of diagonally opposing white and black squares. If determined automatically, this sampling window will usually be 23x23 pixels. It is the sampling of this large image region that allows the function to return the corner position to subpixel resolution.
If verify.file
is non-NULL
, the internal corners are overlayed on the input image to verify that the correct corners have been found and in the correct order. The first corner is circled in red, a green line interconnects all the intermediate corners in sequence and the last corner is circled in blue (the order of colors then being RGB).
An array of the pixel coordinates of internal corners to subpixel resolution in an array of two (one checkerboard input), three (if image.file
is a vector) or four dimensions (if image.file
is a matrix). For images in which the expected number of internal corners were not found, an NA
matrix is returned for those particular images. The corners are returned along the nx
dimension first and the ny
dimension second.
Aaron Olsen
This function was written based on the methodology described in 'Learning OpenCV' for the automated detection of internal checkerboard corners (Bradski and Kaehler 2008).
readCheckerboardsToArray
, measureCheckerboardSize
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## FIND 5 X 3 INTERNAL CORNERS IN A SINGLE IMAGE corners <- findCheckerboardCorners(image.file=paste0(fdir, "Checkerboards/RUlna.JPG"), perim.min=180, nx=5, ny=3) ## FIND 5 X 3 INTERNAL CORNERS IN ALL IMAGES IN A FOLDER (HERE 3) corners <- findCheckerboardCorners(image.file=paste0(fdir, "Checkerboards"), perim.min=180, nx=5, ny=3) ## WHICH DIMENSIONS ARE ASSIGNED TO NX AND NY IS ARBITRARY BUT REVERSING ## THESE WILL CHANGE THE SEQUENCE IN WHICH THE CORNERS ARE RETURNED corners <- findCheckerboardCorners(image.file=paste0(fdir, "Checkerboards/RUlna.JPG"), perim.min=180, nx=3, ny=5)
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## FIND 5 X 3 INTERNAL CORNERS IN A SINGLE IMAGE corners <- findCheckerboardCorners(image.file=paste0(fdir, "Checkerboards/RUlna.JPG"), perim.min=180, nx=5, ny=3) ## FIND 5 X 3 INTERNAL CORNERS IN ALL IMAGES IN A FOLDER (HERE 3) corners <- findCheckerboardCorners(image.file=paste0(fdir, "Checkerboards"), perim.min=180, nx=5, ny=3) ## WHICH DIMENSIONS ARE ASSIGNED TO NX AND NY IS ARBITRARY BUT REVERSING ## THESE WILL CHANGE THE SEQUENCE IN WHICH THE CORNERS ARE RETURNED corners <- findCheckerboardCorners(image.file=paste0(fdir, "Checkerboards/RUlna.JPG"), perim.min=180, nx=3, ny=5)
This function translates and rotates one point set, optimally aligning it with another point set.
findOptimalPointAlignment(m1, m2, sign = NULL)
findOptimalPointAlignment(m1, m2, sign = NULL)
m1 |
a point set matrix |
m2 |
a second point set matrix of the same dimensions as |
sign |
Used for debugging. |
This function optimally aligns point set m2
with point set m1
. m1
and m2
must contain the exact same landmarks or points in the same order. Points present in m2
but not m1
should be NA
in m1
. They do not need to be NA
in m2
; all translations and rotations will be applied to all points in m2
even though only shared points will be used in the alignment.
The function first centers the centroid m2
about the centroid of m1
. The function svd()
is then used to find the 3D rotation matrix that optimally aligns m2
to m1
based on common points. The positions of points in m2
relative to one another are unchanged. Thus, optimal rotation is constrained to already translated point sets. Depending on the point sets, a better alignment may be possible by allowing translation and rotation to be optimized simultaneously.
This function is called by unifyLandmarks
to align landmark sets and by dltTestCalibration
to test accuracy in reconstructed calibration grids.
m2
after alignment.
Modified from unifyVD()
by Annat Haber.
Annat Haber, Aaron Olsen
Rohlf, F.J. (1990) "Chapter 10. Rotational fit (Procrustes) Methods." Proceedings of the Michigan Morphometrics Workshop. Ed. F. James Rohlf and Fred L. Bookstein. The University of Michigan Museum of Zoology, 1990. 227–236. Info page at lib.umich.edu
## MAKE MATRIX OF 3D POINTS m1 <- matrix(c(0,0,0, 1,3,2, 4,2,1, 5,5,3, 1,4,2, 3,6,4), nrow=6, ncol=3) ## COPY TO M2 m2 <- m1 ## MAKE MISSING POINT IN M1 ## ALTHOUGH NOT USED IN THE ALIGNMENT THE CORRESPONDING POINT ## IN M2 IS STILL RETURNED AFTER ALIGNMENT m1[3, ] <- NA ## CENTER M2 ABOUT CE m2 <- m2 %*% rotationMatrixZYX_SM(pi/6, -pi/3, pi/8) ## TRANSLATE M2 m2 <- m2 + matrix(c(2,3,4), nrow=6, ncol=3, byrow=TRUE) ## ALIGN M2 TO M1 m3 <- findOptimalPointAlignment(m1, m2) ## NOTE THAT RETURNED MATRIX IS IDENTICAL TO M1 ## OF COURSE REAL WORLD DATA WILL HAVE SOME ERROR m1 m3
## MAKE MATRIX OF 3D POINTS m1 <- matrix(c(0,0,0, 1,3,2, 4,2,1, 5,5,3, 1,4,2, 3,6,4), nrow=6, ncol=3) ## COPY TO M2 m2 <- m1 ## MAKE MISSING POINT IN M1 ## ALTHOUGH NOT USED IN THE ALIGNMENT THE CORRESPONDING POINT ## IN M2 IS STILL RETURNED AFTER ALIGNMENT m1[3, ] <- NA ## CENTER M2 ABOUT CE m2 <- m2 %*% rotationMatrixZYX_SM(pi/6, -pi/3, pi/8) ## TRANSLATE M2 m2 <- m2 + matrix(c(2,3,4), nrow=6, ncol=3, byrow=TRUE) ## ALIGN M2 TO M1 m3 <- findOptimalPointAlignment(m1, m2) ## NOTE THAT RETURNED MATRIX IS IDENTICAL TO M1 ## OF COURSE REAL WORLD DATA WILL HAVE SOME ERROR m1 m3
This function is used to fit a model of points at a regular interval to a sample of points in one dimension. The function is used by measureCheckerboardSize
to estimate the solution to the inter-point distance of points along a line or in a grid.
gridPointsFit(p, nx, ny=NULL)
gridPointsFit(p, nx, ny=NULL)
p |
The parameters defining the regular point distribution. When |
nx |
The number of points to be created at regular spacing along one dimension. |
ny |
The number of points to be created at regular spacing along a second dimension. |
This function is used to fit a model of points at a regular interval to a sample of points in one dimension. The function is used by measureCheckerboardSize
to estimate the solution to the inter-point distance of points along a line or in a grid. To fit a model to points along lines and grids in two dimensions, each dimension is fit separately. A best fit estimate of the true interval between points can then be calculated from the optimized parameters. See the examples below for how to use gridPointsFit()
to estimate the inter-point intervals of line and grid points.
a vector of length nx*ny
.
Aaron Olsen
## ESTIMATE LINE INTER-POINT INTERVAL # GENERATE POINTS AT A REGULAR INTERVAL WITH NORMAL, RANDOM VARIATION pts <- cbind((1:500) + rnorm(500, sd=1), (1:500) + rnorm(500, sd=1)) # FIND THE MEAN SUCCESSIVE POINT-TO-DISTANCE # NOTE THAT THIS CONSISTENTLY OVERESTIMATES THE TRUE INTERVAL mean(sqrt(rowSums((pts[2:nrow(pts), ] - pts[1:(nrow(pts)-1), ])^2))) # FIT A REGULARLY SPACED POINTS MODEL TO EACH DIMENSION OF THE POINTS MATRIX fit_x <- nlminb(start=c(pts[1, 1], pts[2, 1]-pts[1, 1]), objective=gridPointsFitError, nx=nrow(pts), points=pts[, 1]) fit_y <- nlminb(start=c(pts[1, 2], pts[2, 2]-pts[1, 2]), objective=gridPointsFitError, nx=nrow(pts), points=pts[, 2]) # FIND THE BEST FIT INTER-POINT DISTANCE # MORE ACCURATELY RECOVERS TRUE INTERVAL sqrt(fit_x$par[2]^2 + fit_y$par[2]^2) ## ESTIMATE REGULAR GRID SQUARE SIZE # GENERATE A REGULAR GRID WITH NORMAL, RANDOM VARIATION corners <- cbind( rep(1:20, 20) + rnorm(20^2, sd=0.1), c(t(matrix(1:20, nrow=20, ncol=20))) + rnorm(20^2, sd=0.1)) # FIT A REGULARLY SPACED POINTS MODEL TO EACH DIMENSION OF THE POINTS MATRIX fit_x <- nlminb( start=c(corners[1, 1], corners[2, 1]-corners[1, 1], 0), objective=gridPointsFitError, points=corners[, 1], nx=20, ny=20) fit_y <- nlminb( start=c(corners[1, 2], corners[2, 2]-corners[1, 2], 0), objective=gridPointsFitError, points=corners[, 2], nx=20, ny=20) # FIND THE BEST FIT INTER-POINT DISTANCE (SQUARE SIZE) sqrt(fit_x$par[2]^2 + fit_y$par[2]^2)
## ESTIMATE LINE INTER-POINT INTERVAL # GENERATE POINTS AT A REGULAR INTERVAL WITH NORMAL, RANDOM VARIATION pts <- cbind((1:500) + rnorm(500, sd=1), (1:500) + rnorm(500, sd=1)) # FIND THE MEAN SUCCESSIVE POINT-TO-DISTANCE # NOTE THAT THIS CONSISTENTLY OVERESTIMATES THE TRUE INTERVAL mean(sqrt(rowSums((pts[2:nrow(pts), ] - pts[1:(nrow(pts)-1), ])^2))) # FIT A REGULARLY SPACED POINTS MODEL TO EACH DIMENSION OF THE POINTS MATRIX fit_x <- nlminb(start=c(pts[1, 1], pts[2, 1]-pts[1, 1]), objective=gridPointsFitError, nx=nrow(pts), points=pts[, 1]) fit_y <- nlminb(start=c(pts[1, 2], pts[2, 2]-pts[1, 2]), objective=gridPointsFitError, nx=nrow(pts), points=pts[, 2]) # FIND THE BEST FIT INTER-POINT DISTANCE # MORE ACCURATELY RECOVERS TRUE INTERVAL sqrt(fit_x$par[2]^2 + fit_y$par[2]^2) ## ESTIMATE REGULAR GRID SQUARE SIZE # GENERATE A REGULAR GRID WITH NORMAL, RANDOM VARIATION corners <- cbind( rep(1:20, 20) + rnorm(20^2, sd=0.1), c(t(matrix(1:20, nrow=20, ncol=20))) + rnorm(20^2, sd=0.1)) # FIT A REGULARLY SPACED POINTS MODEL TO EACH DIMENSION OF THE POINTS MATRIX fit_x <- nlminb( start=c(corners[1, 1], corners[2, 1]-corners[1, 1], 0), objective=gridPointsFitError, points=corners[, 1], nx=20, ny=20) fit_y <- nlminb( start=c(corners[1, 2], corners[2, 2]-corners[1, 2], 0), objective=gridPointsFitError, points=corners[, 2], nx=20, ny=20) # FIND THE BEST FIT INTER-POINT DISTANCE (SQUARE SIZE) sqrt(fit_x$par[2]^2 + fit_y$par[2]^2)
This function takes parameters describing a 3D planar grid projected onto a 2D image plane and returns a grid of specified dimensions. Users will probably not call this function directly. Rather, it is used by resampleGridImagePoints
to produce grid points with the same transformations as an imaged grid but with fewer points.
imagePlaneGridTransform(p, nx, ny)
imagePlaneGridTransform(p, nx, ny)
p |
a vector of 12 grid parameters. The first eight values are the x,y-coordinates of the four grid corners ( |
nx |
the number of points along the first dimension. Note that although the grid can have a different number of rows than columns, the grid units themselves should be square (of uniform size in both dimensions). |
ny |
the number of points along the second dimension. |
When taking a photo of planar grid points (such as the internal corners of a checkerboard pattern) arbitrarily oriented in 3D space, the distribution of grid points in a 2D photograph will reflect several transformations. The grid may be translated to any position within the image plane and rotated by any angle. Additionally, if the grid plane is not parallel to the image plane, perspective effects will cause points further away to appear closer together. When arbitrary 3D position and perspective effects are combined, the transformation of a planar grid can be quite extreme (see example).
imagePlaneGridTransform()
takes 12 parameters describing these effects and applies them to a grid of the specified dimensions, returning the transformed grid points. The first eight parameters are the x,y-coordinates of the four grid corners (x1
, y1
, x2
, y2
, etc.). The ninth and tenth parameters describe how interpoint spacing changes from row-to-row and column-to-column, respectively. This is the a
parameter in the function quadraticPointsOnInterval
. A value of zero indicates uniform spacing between consecutive points across all rows while values less or greater than zero indicates points that become closer together or further apart from one row or column to the next. The eleventh and twelfth parameters are analogous to the ninth and tenth parameters but describe how spacing changes between rows and columns instead of between points. These last two parameters are also the a
parameter in the function quadraticPointsOnInterval
.
Currently, imagePlaneGridTransform()
does not currently account for lens distortion (e.g. barrel, pincushion, etc.). If distortion is significant, users should undistort the photographs prior to using imagePlaneGridTransform()
. It is hoped that future versions will include additional parameters to account for lens distortion.
Users will probably not call imagePlaneGridTransform()
directly. In this package, this function is used by resampleGridImagePoints
to both fit transformation parameters to a matrix of imaged grid points and to produce a transformed grid consisting of fewer points. In this way, fewer points (but representing the same amount of information) can be used in more computationally intensive steps.
a matrix of transformed grid points.
Aaron Olsen
resampleGridImagePoints
, quadraticPointsOnInterval
, imagePlaneGridTransformError
## SET GRID PARAMETERS ## THE FIRST 8 NUMBERS ARE CORNERS ## THE LAST 4 NUMBERS ARE TRANSFORMATION PARAMETERS p <- c(3656, 379, 707, 264, 383, 1034, 3984, 1164, 63.772, -25.211, -0.818, -3.339) ## CREATE TRANSFORMED GRID grid <- imagePlaneGridTransform(p=p, nx=21, ny=14) ## PLOT GRID plot(grid) ## MARK CORNERS OF GRID FROM p points(matrix(p[1:8], nrow=4, ncol=2, byrow=TRUE), col='red', lwd=2, cex=1.5)
## SET GRID PARAMETERS ## THE FIRST 8 NUMBERS ARE CORNERS ## THE LAST 4 NUMBERS ARE TRANSFORMATION PARAMETERS p <- c(3656, 379, 707, 264, 383, 1034, 3984, 1164, 63.772, -25.211, -0.818, -3.339) ## CREATE TRANSFORMED GRID grid <- imagePlaneGridTransform(p=p, nx=21, ny=14) ## PLOT GRID plot(grid) ## MARK CORNERS OF GRID FROM p points(matrix(p[1:8], nrow=4, ncol=2, byrow=TRUE), col='red', lwd=2, cex=1.5)
Returns the mean error between a matrix of grid points and a matrix of transformed grid points (produced by imagePlaneGridTransform
). This function is called internally by the function resampleGridImagePoints
in evaluating the goodness of fit between imaged grid points and grid points produced by an image perspective model.
imagePlaneGridTransformError(p, nx, ny, grid)
imagePlaneGridTransformError(p, nx, ny, grid)
p |
a vector of 12 grid parameters (see |
nx |
the number of points along the first dimension. |
ny |
the number of points along the second dimension. |
grid |
a matrix of grid points to be compared against the model grid points. |
the mean error.
Aaron Olsen
imagePlaneGridTransform
, resampleGridImagePoints
Converts a landmark list to a landmark matrix. The landmark matrix is identical to the matrix that would be returned if the landmark files were sent directly to readLandmarksToMatrix
.
landmarkListToMatrix(lm.list)
landmarkListToMatrix(lm.list)
lm.list |
a landmark list. See |
a landmark matrix.
Aaron Olsen
readLandmarksToList
, readLandmarksToMatrix
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD - TWO DIFFERENT 3D POINT SETS file <- paste0(fdir, "lm_3d_even_a", 1:2, ".txt") ## READ LANDMARKS INTO A LIST lm.list <- readLandmarksToList(file=file, row.names=1) ## CONVERT LANDMARK LIST TO LANDMARK MATRIX lm.matrix <- landmarkListToMatrix(lm.list) lm.matrix
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD - TWO DIFFERENT 3D POINT SETS file <- paste0(fdir, "lm_3d_even_a", 1:2, ".txt") ## READ LANDMARKS INTO A LIST lm.list <- readLandmarksToList(file=file, row.names=1) ## CONVERT LANDMARK LIST TO LANDMARK MATRIX lm.matrix <- landmarkListToMatrix(lm.list) lm.matrix
Converts a landmark matrix to a landmark list.
landmarkMatrixToList(lm.matrix, semilandmark.pattern='[0-9]+$', k=ncol(lm.matrix))
landmarkMatrixToList(lm.matrix, semilandmark.pattern='[0-9]+$', k=ncol(lm.matrix))
lm.matrix |
a landmark matrix. See |
semilandmark.pattern |
a regular expression pattern passed to |
k |
the number of dimensions of the landmark data. |
a landmark list.
Aaron Olsen
landmarkListToMatrix
, readLandmarksToList
, readLandmarksToMatrix
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD - TWO DIFFERENT 3D POINT SETS file <- paste0(fdir, "lm_2d_a1_v", 1:2, ".txt") ## READ LANDMARKS INTO A LIST lm.matrix <- readLandmarksToMatrix(file=file, row.names=1) ## CONVERT LANDMARK LIST TO LANDMARK MATRIX lm.list <- landmarkMatrixToList(lm.matrix, k=2) ## CAN BE CONVERTED BACK INTO MATRIX ## RECOVERING THE SAME MATRIX AS THE ORIGINAL lm.matrix <- landmarkListToMatrix(lm.list)
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD - TWO DIFFERENT 3D POINT SETS file <- paste0(fdir, "lm_2d_a1_v", 1:2, ".txt") ## READ LANDMARKS INTO A LIST lm.matrix <- readLandmarksToMatrix(file=file, row.names=1) ## CONVERT LANDMARK LIST TO LANDMARK MATRIX lm.list <- landmarkMatrixToList(lm.matrix, k=2) ## CAN BE CONVERTED BACK INTO MATRIX ## RECOVERING THE SAME MATRIX AS THE ORIGINAL lm.matrix <- landmarkListToMatrix(lm.list)
This function estimates the square size of a checkerboard, optionally scaling this to real-world units (e.g. millimeters).
measureCheckerboardSize(corner.file, nx, ruler.file=NULL, ruler.pt.size=NULL) ## S3 method for class 'measureCheckerboardSize' summary(object, ...)
measureCheckerboardSize(corner.file, nx, ruler.file=NULL, ruler.pt.size=NULL) ## S3 method for class 'measureCheckerboardSize' summary(object, ...)
corner.file |
a file path to text file containing a matrix of internal corners from a checkerboard pattern (a point grid) or the matrix itself. The text file must not have row names or a header. |
nx |
the number of internal corners in the first dimension along which the checkerboard points are ordered. |
ruler.file |
a file path to a text file containing a matrix of evenly spaced points digitized along a ruler (or comparable standard) or the matrix itself. The text file must have row names but no header or column names. |
ruler.pt.size |
the size of the spacing between points in the |
object |
a list of class |
... |
further arguments passed to other methods. |
corner.file
can be a file path to a text file containing a matrix of internal corners from a checkerboard pattern (ie points in a regular grid pattern) or the matrix itself. These can be automatically detected from a JPEG image using the function findCheckerboardCorners
. The function first fits a camera perspective model to the corner points to robustly compare the opposing side lengths of the grid (see resampleGridImagePoints
). These are returned as side.lengths
and are displayed when calling the summary method. Opposing sides that differ greatly in length indicate that the grid was not completely flat relative to the image plane when it was photographed.
measureCheckerboardSize()
then estimates the checkerboard or grid square size by fitting a simple grid model to the points (see gridPointsFit
). The best fitting parameters are used to estimate the square size. Model fitting is more robust to noise in the grid point coordinates than taking the mean inter-point distance, for instance. The model goodness of fit can be assessed by the returned elements dist.corner.fit.mean
and dist.corner.fit.sd
.
ruler.file
can be a file path to a text file containing a matrix of points at equal intervals along a ruler or the matrix itself. These ruler points can be digitized from an image using the function digitizeImage
. If ruler.file
is NULL
, then only the checkerboard square size (in the input units) is returned. All other return values are NULL
. If ruler.file
is non-NULL
, the distance between consecutive ruler points (the ruler point interval) is estimated by fitting a model of points at a regular interval along a line (see gridPointsFit
). The goodness of fit for the ruler point model can be assessed by the returned elements dist.ruler.fit.mean
and dist.ruler.fit.sd
. The estimated ruler point interval is used to scale the checkerboard square size to the units of ruler.pt.size
.
ruler.pt.size
can be numeric or alphanumeric (including the units). For example, '1'
, '1 mm'
and '1.0 mm'
are all possible inputs to ruler.pt.size
. The units are automatically extracted and only used in the summary function to help interpret the function results. measureCheckerboardSize()
also returns the estimated real-world size of a pixel. This represents the resolution of the camera at the surface of the checkerboard pattern.
a list of class "measureCheckerboardSize"
with the following elements:
side.lengths |
the lengths of the four sides of the grid estimated by camera perspective model fitting. |
dist.corner.fit.mean |
the mean difference between the corner points |
dist.corner.fit.sd |
the standard deviation in the difference between the corner points |
square.size.px |
the best-fit estimate of the checkerboard square size in pixels. |
square.size.rwu |
the best-fit estimate of the checkerboard square size in real-world units. |
dist.ruler.fit.mean |
the mean difference between the |
dist.ruler.fit.sd |
the standard deviation in the difference between the |
ruler.size.px |
the best-fit estimate of the distance between consecutive points on the ruler (in pixels) in the plane of the imaged grid. |
rwu.per.px |
the real-world size of a pixel in the image (the length of one side of the pixel) in the plane of the imaged grid. |
unit |
if |
Aaron Olsen
drawCheckerboard
, resampleGridImagePoints
, gridPointsFit
, digitizeImage
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CHECKERBOARD POINTS FILE corner_file <- paste0(fdir, "checker_21_14_200(9).txt") ## NUMBER OF INTERNAL CORNERS IN THE HORIZONTAL DIMENSION nx <- 21 ## NUMBER OF INTERNAL CORNERS IN THE VERTICAL DIMENSION ny <- 14 ## SET FILE PATH TO RULER POINTS FILE ruler_file <- paste0(fdir, "ruler_21_14_200(9).txt") ## ESTIMATE SQUARE SIZE square_size <- measureCheckerboardSize(corner.file=corner_file, nx=nx) ## PRINT SUMMARY summary(square_size) ## ESTIMATE SQUARE SIZE AND SCALE WITH RULER POINTS square_size_scale <- measureCheckerboardSize(corner.file=corner_file, nx=nx, ruler.file=ruler_file, ruler.pt.size='1 mm') ## PRINT SUMMARY summary(square_size_scale) ## Not run: ## INPUT MATRICES DIRECTLY ## READ POINTS INTO MATRICES corner_pts <- as.matrix(read.table(corner_file)) ruler_pts <- as.matrix(read.table(ruler_file, row.names=1)) ## ESTIMATE SQUARE SIZE AND SCALE WITH RULER POINTS square_size_scale <- measureCheckerboardSize(corner.file=corner_pts, nx=nx, ruler.file=ruler_pts, ruler.pt.size='1 mm') ## End(Not run)
## GET THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILE PATH TO CHECKERBOARD POINTS FILE corner_file <- paste0(fdir, "checker_21_14_200(9).txt") ## NUMBER OF INTERNAL CORNERS IN THE HORIZONTAL DIMENSION nx <- 21 ## NUMBER OF INTERNAL CORNERS IN THE VERTICAL DIMENSION ny <- 14 ## SET FILE PATH TO RULER POINTS FILE ruler_file <- paste0(fdir, "ruler_21_14_200(9).txt") ## ESTIMATE SQUARE SIZE square_size <- measureCheckerboardSize(corner.file=corner_file, nx=nx) ## PRINT SUMMARY summary(square_size) ## ESTIMATE SQUARE SIZE AND SCALE WITH RULER POINTS square_size_scale <- measureCheckerboardSize(corner.file=corner_file, nx=nx, ruler.file=ruler_file, ruler.pt.size='1 mm') ## PRINT SUMMARY summary(square_size_scale) ## Not run: ## INPUT MATRICES DIRECTLY ## READ POINTS INTO MATRICES corner_pts <- as.matrix(read.table(corner_file)) ruler_pts <- as.matrix(read.table(ruler_file, row.names=1)) ## ESTIMATE SQUARE SIZE AND SCALE WITH RULER POINTS square_size_scale <- measureCheckerboardSize(corner.file=corner_pts, nx=nx, ruler.file=ruler_pts, ruler.pt.size='1 mm') ## End(Not run)
Given a 2D or 3D input point p
and a 2D or 3D line, this function finds a point on the line at a minimum distance from point p
. This is equivalent to the orthogonal projection of point p
onto the line.
orthogonalProjectionToLine(p, l1 = NULL, l2 = NULL)
orthogonalProjectionToLine(p, l1 = NULL, l2 = NULL)
p |
a vector of a single point or a matrix of multiple points |
l1 |
a vector describing a point on a line or a list with line constants |
l2 |
if |
If p
is a vector, the function returns a point as a vector of the same dimension. If p
is a matrix, each row is treated as a point and the orthogonal projection is returned for each. These points are returned as a matrix (of the same dimension), each row being the orthogonal projection of the corresponding row in p
.
The line input can be defined using one of three standard ways: two points on the line, 'm' and 'b' constants (slope and y-intercept) and direction numbers 'abc' (a vector parallel to a line through the origin). If l1
is a vector, this is taken as one point on the line and l2
must be a second point on the line. If l1
is a list, the named objects must correspond to one of these three line definitions. Two points on the line are defined as l1$l1
and l1$l2
. 'm' and 'b' are defined as l1$m
and l1$b
. And the direction numbers 'abc' are defined as l1$a
, l1$b
and l1$c
.
a vector if p
is a vector and a matrix if p
is a matrix. The returned vector or matrix will be of the same dimensions as p
.
Aaron Olsen
http://paulbourke.net/geometry/pointlineplane/
## POINT INPUT: 2D VECTOR ## LINE INPUT: l1, l2 ## LINE THROUGH THE ORIGIN WITH SLOPE OF ONE orthogonalProjectionToLine(p=c(0, 5), l1=c(0, 0), l2=c(3, 3)) ## POINT INPUT: 2D VECTOR ## LINE INPUT: LIST WITH l1, l2 orthogonalProjectionToLine(p=c(0, 5), l1=list(l1=c(0, 0), l2=c(3, 3))) ## POINT INPUT: 2D VECTOR ## LINE INPUT: LIST WITH m, b ## LINE WITH Y-INTERCEPT AT ONE AND SLOPE OF ONE orthogonalProjectionToLine(p=c(0, 5), l1=list(m=1, b=0)) ## POINT INPUT: 2D VECTOR ## LINE INPUT: LIST WITH VECTOR PARALLEL TO LINE THROUGH THE ORIGIN ## LINE THROUGH THE ORIGIN WITH SLOPE OF ONE orthogonalProjectionToLine(p=c(0, 5), l1=list(a=1, b=-1, c=0)) ## POINT INPUT: 2D VECTOR ## LINE INPUT: SAME AS PREVIOUS BUT WITH Z-AXIS COMPONENT orthogonalProjectionToLine(p=c(0, 5), l1=list(a=1, b=-1, c=1)) ## POINT INPUT: 3D VECTOR ## LINE INPUT: l1, l2 orthogonalProjectionToLine(p=c(0, 5, 0), l1=list(l1=c(0, 0, 0), l2=c(3, 3, 3))) ## POINT INPUT: 2D MATRIX ## LINE INPUT: l1, l2 p <- matrix(c(0,5, 0,10), nrow=2, byrow=TRUE) orthogonalProjectionToLine(p=p, l1=list(l1=c(0, 0), l2=c(3, 3))) ## POINT INPUT: 3D MATRIX ## LINE INPUT: l1, l2 p <- matrix(c(0,5,0, 0,10,0), nrow=2, byrow=TRUE) orthogonalProjectionToLine(p=p, l1=list(l1=c(0, 0, 0), l2=c(3, 3, 3)))
## POINT INPUT: 2D VECTOR ## LINE INPUT: l1, l2 ## LINE THROUGH THE ORIGIN WITH SLOPE OF ONE orthogonalProjectionToLine(p=c(0, 5), l1=c(0, 0), l2=c(3, 3)) ## POINT INPUT: 2D VECTOR ## LINE INPUT: LIST WITH l1, l2 orthogonalProjectionToLine(p=c(0, 5), l1=list(l1=c(0, 0), l2=c(3, 3))) ## POINT INPUT: 2D VECTOR ## LINE INPUT: LIST WITH m, b ## LINE WITH Y-INTERCEPT AT ONE AND SLOPE OF ONE orthogonalProjectionToLine(p=c(0, 5), l1=list(m=1, b=0)) ## POINT INPUT: 2D VECTOR ## LINE INPUT: LIST WITH VECTOR PARALLEL TO LINE THROUGH THE ORIGIN ## LINE THROUGH THE ORIGIN WITH SLOPE OF ONE orthogonalProjectionToLine(p=c(0, 5), l1=list(a=1, b=-1, c=0)) ## POINT INPUT: 2D VECTOR ## LINE INPUT: SAME AS PREVIOUS BUT WITH Z-AXIS COMPONENT orthogonalProjectionToLine(p=c(0, 5), l1=list(a=1, b=-1, c=1)) ## POINT INPUT: 3D VECTOR ## LINE INPUT: l1, l2 orthogonalProjectionToLine(p=c(0, 5, 0), l1=list(l1=c(0, 0, 0), l2=c(3, 3, 3))) ## POINT INPUT: 2D MATRIX ## LINE INPUT: l1, l2 p <- matrix(c(0,5, 0,10), nrow=2, byrow=TRUE) orthogonalProjectionToLine(p=p, l1=list(l1=c(0, 0), l2=c(3, 3))) ## POINT INPUT: 3D MATRIX ## LINE INPUT: l1, l2 p <- matrix(c(0,5,0, 0,10,0), nrow=2, byrow=TRUE) orthogonalProjectionToLine(p=p, l1=list(l1=c(0, 0, 0), l2=c(3, 3, 3)))
This function takes a matrix of points, calculates the cumulative distance from start to end and then uses the cumulative distance and intermediate points to generate evenly spaced points between the start and end points. Linear interpolation is used between neighboring points, so the returned points will either coincide with the input points or fall on straight lines between consecutive points.
pointsAtEvenSpacing(x, n)
pointsAtEvenSpacing(x, n)
x |
a matrix or landmark list of points of any number of dimensions. If input is a list, only the first element is used. |
n |
the number of points to generate, including the start and end points. |
The function first removes all NA
values. Then, the cumulative distance is calculated from the first to last point. The last value is taken as the total length of the line or curve, defined by matrix x
. This total length is divided by n-1
to find a uniform segment length that will separate n
evenly spaced points, including the first and last non-NA
values in x
.
The function iterates through x
, finding the point that is at a distance equal to or just less than the segment length from the previous point. If the selected point is at a distance less than the segment length from the previous point, a point is chosen on the line between this point and the next to complete the full segment length. In this way, returned points will either coincide with the input points or fall on straight lines between consecutive points.
In the simplest implementation, pointsAtEvenSpacing()
can be used for linear interpolation (see first example below). Define the start and end points in x
as a two-row matrix and then select the number of points to include on the line.
If x
represents densely sampled points on a curve (see the second example below) and if the curve can be approximated by straight lines between consecutive points, then pointsAtEvenSpacing()
will provide comparable results to other methods, such as function fitting. This is especially useful for curves not easily fit by a mathematical function.
a matrix of n
points. The start and end points correspond to the first and last non-NA values in x
.
Aaron Olsen
imagePlaneGridTransform
, resampleGridImagePoints
, imagePlaneGridTransformError
## LINEAR INTERPOLATION ## ## CREATE A MATRIX OF TWO POINTS two_points <- matrix(c(0, 10, 0, 10), nrow=2, ncol=2) ## GENERATE 20 POINTS ALONG THE LINE pts_aes <- pointsAtEvenSpacing(x=two_points, n=20) ## PLOT THE LINE plot(two_points, type='l') ## AND THE POINTS ALONG THE LINE points(pts_aes, col='red') ## POINTS ALONG A CURVE ## ## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET 3D LANDMARK AND CURVE POINT FILE AND READ INTO A MATRIX lm.matrix <- readLandmarksToMatrix(paste0(fdir, "lm_3d_a2.txt"), row.names=1) ## PLOT THE LANDMARKS AND CURVE POINTS pts <- na.omit(lm.matrix) r <- abs(apply(pts, 2, 'max') - apply(pts, 2, 'min')) ## Not run: ## PLOT USING THE RGL PACKAGE plot3d(pts, aspect=c(r[1]/r[3], r[2]/r[3], 1), size=0.5) ## End(Not run) ## CONVERT LANDMARKS TO LIST FORMAT TO EASILY ACCESS CURVE POINTS lm.list <- landmarkMatrixToList(lm.matrix) ## CREATE 10 EVENLY SPACED POINTS ALONG ONE CURVE lm.list$pterygoid_crest_R <- pointsAtEvenSpacing(x=lm.list$pterygoid_crest_R, n=10) ## CREATE 15 ALONG ANOTHER lm.list$tomium_R <- pointsAtEvenSpacing(x=lm.list$tomium_R, n=15) ## CONVERT BACK TO MATRIX lm.matrix <- landmarkListToMatrix(lm.list) ## Not run: ## PLOT NEW EVENLY SPACED POINTS WITH PREVIOUS POINTS plot3d(lm.matrix, add=T, size=4, col='red') ## End(Not run)
## LINEAR INTERPOLATION ## ## CREATE A MATRIX OF TWO POINTS two_points <- matrix(c(0, 10, 0, 10), nrow=2, ncol=2) ## GENERATE 20 POINTS ALONG THE LINE pts_aes <- pointsAtEvenSpacing(x=two_points, n=20) ## PLOT THE LINE plot(two_points, type='l') ## AND THE POINTS ALONG THE LINE points(pts_aes, col='red') ## POINTS ALONG A CURVE ## ## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET 3D LANDMARK AND CURVE POINT FILE AND READ INTO A MATRIX lm.matrix <- readLandmarksToMatrix(paste0(fdir, "lm_3d_a2.txt"), row.names=1) ## PLOT THE LANDMARKS AND CURVE POINTS pts <- na.omit(lm.matrix) r <- abs(apply(pts, 2, 'max') - apply(pts, 2, 'min')) ## Not run: ## PLOT USING THE RGL PACKAGE plot3d(pts, aspect=c(r[1]/r[3], r[2]/r[3], 1), size=0.5) ## End(Not run) ## CONVERT LANDMARKS TO LIST FORMAT TO EASILY ACCESS CURVE POINTS lm.list <- landmarkMatrixToList(lm.matrix) ## CREATE 10 EVENLY SPACED POINTS ALONG ONE CURVE lm.list$pterygoid_crest_R <- pointsAtEvenSpacing(x=lm.list$pterygoid_crest_R, n=10) ## CREATE 15 ALONG ANOTHER lm.list$tomium_R <- pointsAtEvenSpacing(x=lm.list$tomium_R, n=15) ## CONVERT BACK TO MATRIX lm.matrix <- landmarkListToMatrix(lm.list) ## Not run: ## PLOT NEW EVENLY SPACED POINTS WITH PREVIOUS POINTS plot3d(lm.matrix, add=T, size=4, col='red') ## End(Not run)
Generates a specified number of points on an interval, applying a quadratic function to interpoint spacing. This function is called internally by imagePlaneGridTransform
.
quadraticPointsOnInterval(t1, t2, n, a)
quadraticPointsOnInterval(t1, t2, n, a)
t1 |
the starting value of the returned points. |
t2 |
the final value of the returned points. |
n |
the number of points. |
a |
a quadratic parameter describing how interpoint spacing changes over the interval. |
The parameter a
describes how strong of a skew to place on the interpoint distances over the interval specified by t1
and t2
. When a=0
, the points are spaced uniformly across the interval. When a>0
or a<0
, points become further apart or closer together along the interval, respectively, at the rate of a quadratic function (see "Examples").
a vector of points.
Aaron Olsen
imagePlaneGridTransform
, resampleGridImagePoints
, imagePlaneGridTransformError
## GENERATE EVENLY SPACED POINTS ON INTERVAL q0 <- quadraticPointsOnInterval(t1=0, t2=1, n=10, a=0) ## MAKE POINTS PROGRESSIVELY FURTHER APART ALONG INTERVAL qgt0 <- quadraticPointsOnInterval(t1=0, t2=1, n=10, a=1) ## MAKE POINTS PROGRESSIVELY CLOSER TOGETHER ALONG INTERVAL qlt0 <- quadraticPointsOnInterval(t1=0, t2=1, n=10, a=-1) ## PLOT POINTS ON THREE SEPARATE LINES plot(q0, rep(0, 10)) points(qgt0, rep(0.5, 10), col='green') points(qlt0, rep(-0.5, 10), col='blue')
## GENERATE EVENLY SPACED POINTS ON INTERVAL q0 <- quadraticPointsOnInterval(t1=0, t2=1, n=10, a=0) ## MAKE POINTS PROGRESSIVELY FURTHER APART ALONG INTERVAL qgt0 <- quadraticPointsOnInterval(t1=0, t2=1, n=10, a=1) ## MAKE POINTS PROGRESSIVELY CLOSER TOGETHER ALONG INTERVAL qlt0 <- quadraticPointsOnInterval(t1=0, t2=1, n=10, a=-1) ## PLOT POINTS ON THREE SEPARATE LINES plot(q0, rep(0, 10)) points(qgt0, rep(0.5, 10), col='green') points(qlt0, rep(-0.5, 10), col='blue')
Reads Bezier control points from a file or files into a list grouped first by curve name and then by the index of the file from which they were read. A separate function from the standard read functions is necessary since the number of control points may differ for each Bezier curve or spline and, thus, the number of values may differ by row.
readBezierControlPoints(file, ndim = 2, ...)
readBezierControlPoints(file, ndim = 2, ...)
file |
file(s) to be read. |
ndim |
the number of dimensions of the Bezier curve points |
... |
further arguments to be passed to |
The rows of each file must start with the name of the curve or spline followed by the control points, all separated by tabs. The control points are listed first by dimension and then by point (x1\ty1\tx2\ty2
etc.). For example, three Bezier points starting with [100, 200] would be on one line as follows, with \t
replaced by tabs.
tomium_R\t100\t200\t300\t100\t400\t300
Each Bezier curve or spline is first grouped into a list by curve name (e.g. list$tomium_R
) and then by the index of the file from which it was read (e.g. list$tomium_R[[1]]
from the first file). The control points are made into a matrix where the number of columns corresponds to ndim
. The Bezier list structure is similar to the landmark list structure created by readLandmarksToList
and can be used to generate points along a Bezier curve or spline. See the R package bezier for more details.
a list of Bezier control points grouped by name and file number.
Aaron Olsen
readLandmarksToArray
, readCheckerboardsToArray
, readLandmarksToList
,
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## FILE TO READ file <- paste0(fdir, "bezier_control_points_a2_v", 1:2, ".txt") ## FILE TO READ bcp <- readBezierControlPoints(file=file)
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## FILE TO READ file <- paste0(fdir, "bezier_control_points_a2_v", 1:2, ".txt") ## FILE TO READ bcp <- readBezierControlPoints(file=file)
This function reads grid point matrices into an array from a matrix files allowing for point order reversals along rows, columns, or both.
readCheckerboardsToArray(file, nx, ny, col.reverse = FALSE, row.reverse = FALSE, na.omit=FALSE, ...)
readCheckerboardsToArray(file, nx, ny, col.reverse = FALSE, row.reverse = FALSE, na.omit=FALSE, ...)
file |
a matrix of file paths to be read into an array. Each file path should correspond to a file containing a single landmark matrix. |
nx |
the number of internal corners in the first dimension along which grid points are ordered. |
ny |
the number of internal corners in the second dimension along which grid points are ordered. |
col.reverse |
a logical indicating whether the column order of grid points should be reversed. Can be either single value, a vector or a matrix. |
row.reverse |
a logical indicating whether the row order of grid points should be reversed. Can be either single value, a vector or a matrix. |
na.omit |
whether landmarks with NA values in any file should be omitted. |
... |
further arguments to be passed to |
When using planar grid points to find an optimal stereo calibration, ensuring that the grid point coordinates are listed in the same order from different camera views is challenging. When cameras are viewing the same points from different orientations (e.g. one camera is upside-down relative to another) and when the checkerboard itself is in different orientations, columns and/or rows in one grid point matrix could be flipped relative to another camera view.
readCheckerboardsToArray()
enables correction for this by allowing users to specify whether the rows, columns or both should be reversed after the points are read from a file. If the checkerboard changes orientation within a single camera view it could be necessary to specify row and/or column reversals individually for each file. col.reverse
and row.reverse
can both be either a single logical, a vector of logicals or a matrix of logicals. This allows col.reverse
and row.reverse
to be specified for all files in a vector or matrix or for each file separately. Vector inputs of col.reverse
and row.reverse
with a matrix input of file
will be applied to each column of file
(see last example below).
Row reversal means that point order is reversed along the second dimension (the order along the first dimension is kept intact). Column reversal means that point order is reversed along the first dimension (the order along the second dimension is kept intact). Setting both col.reverse
and row.reverse
to TRUE
is equivalent to reversing the order of points from start to end (row and column structures have no effect). These operations are perhaps best understood through the examples below. For an example of the grid point ordering scheme, also see distanceGridUnits
.
an array of three or four dimensions.
Aaron Olsen
readLandmarksToArray
, readLandmarksToList
, readLandmarksToMatrix
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET NUMBER OF ROWS AND COLUMNS ## THESE ARE THE NUMBER OF INTERNAL CORNERS, NOT THE NUMBER OF SQUARES nx <- 4 ny <- 3 ## SET FILE PATHS file <- matrix(c(paste0(fdir, "rcta_a", 1:3, "_v1.txt"), paste0(fdir, "rcta_a", 1:3, "_v2.txt")), ncol=2) ## READ MATRIX OF FILES ## ## REVERSE COLUMNS IN FIRST COLUMN OF FILE MATRIX ## REVERSE ROWS IN ALL FILES readCheckerboardsToArray(file, nx, ny, col.reverse=c(TRUE, FALSE), row.reverse=TRUE)
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET NUMBER OF ROWS AND COLUMNS ## THESE ARE THE NUMBER OF INTERNAL CORNERS, NOT THE NUMBER OF SQUARES nx <- 4 ny <- 3 ## SET FILE PATHS file <- matrix(c(paste0(fdir, "rcta_a", 1:3, "_v1.txt"), paste0(fdir, "rcta_a", 1:3, "_v2.txt")), ncol=2) ## READ MATRIX OF FILES ## ## REVERSE COLUMNS IN FIRST COLUMN OF FILE MATRIX ## REVERSE ROWS IN ALL FILES readCheckerboardsToArray(file, nx, ny, col.reverse=c(TRUE, FALSE), row.reverse=TRUE)
Reads landmarks from one or more files into a list. This function is useful when dealing with curves (semilandmarks) since curve points can be grouped by curve name for other operations.
readLandmarksToList(file, semilandmark.pattern = "[0-9]+$", ...)
readLandmarksToList(file, semilandmark.pattern = "[0-9]+$", ...)
file |
a single landmark file or vector of landmark files to be read. Each file should contain a single landmark matrix with row names. |
semilandmark.pattern |
a regular expression pattern passed to |
... |
further arguments to be passed to |
This function will read a landmark matrix from one or more files and use the row names in each matrix to match corresponding landmarks into list elements, ordered first by the landmark name and then numbered by the index of the file (in file
) from which the landmark was read. Landmark lists are the required input format for dltMatchCurvePoints
. Landmark lists are also one of three possible input formats for dltReconstruct
and allow for curve points to be easily pulled out of a landmark set for curve fitting.
semilandmark.pattern
is a regular expression passed to sub()
to identify semilandmarks (curve points). By default, the regular expression "[0-9]+$"
identifies row names that end in more than one digit (e.g. 'tomium_R004') as curve points. sub()
removes the part of the string identified by semilandmark.pattern
in order to group all curve points under one curve name (e.g. 'tomium_R004' would be grouped under 'tomium_R'). Curve grouping can be turned off by setting semilandmark.pattern
to ""
. Once grouped, curve points are sorted only by the numeric portion of their row name (identified by semilandmark.pattern
using regexpr
). Preceding zeros are not necessary. For example, after sorting, the order of the following curve points would be: tomium_R1, tomium_R02, tomium_R9, tomium_R10. Note that if these were sorted simply by row name, the order would be: tomium_R02, tomium_R1, tomium_R10, tomium_R9. Landmarks missing from one or more files are given the value NULL
.
The landmark files are read by read.file()
and should thus conform to all requirements of read.file()
. Arguments for read.file()
can be passed through readLandmarksToList()
(e.g. header
, row.names
, etc.).
a landmark list.
Aaron Olsen
readLandmarksToArray
, readLandmarksToMatrix
, readCheckerboardsToArray
,
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD - TWO DIFFERENT 3D POINT SETS file <- paste0(fdir, "lm_3d_even_a", 1:2, ".txt") ## READ LANDMARKS INTO A LIST lm.list <- readLandmarksToList(file=file, row.names=1) ## CURVE POINTS ## CURVE POINTS ARE ABSENT FROM FIRST POINT SET lm.list[['tomium_R']] ## LANDMARKS PRESENT IN BOTH POINT SETS lm.list[['quadrate_jugal_R']] ## LANDMARK MISSING FROM SECOND POINT SET lm.list[['foramen_magnum_inf']]
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD - TWO DIFFERENT 3D POINT SETS file <- paste0(fdir, "lm_3d_even_a", 1:2, ".txt") ## READ LANDMARKS INTO A LIST lm.list <- readLandmarksToList(file=file, row.names=1) ## CURVE POINTS ## CURVE POINTS ARE ABSENT FROM FIRST POINT SET lm.list[['tomium_R']] ## LANDMARKS PRESENT IN BOTH POINT SETS lm.list[['quadrate_jugal_R']] ## LANDMARK MISSING FROM SECOND POINT SET lm.list[['foramen_magnum_inf']]
Reads landmarks from one or more files into a matrix. A single file or vector of files can be input. If more than one file is input, each matrix will be appended to the previous one with matching landmarks in the same row.
readLandmarksToMatrix(file, na.omit = FALSE, ...)
readLandmarksToMatrix(file, na.omit = FALSE, ...)
file |
a single landmark file or vector of landmark files to be read. Each file should contain a single landmark matrix. |
na.omit |
whether landmarks with NA values in any file should be omitted. |
... |
further arguments to be passed to |
This function will read a landmark matrix from one or more files and use the row names in each matrix to match corresponding landmarks into a single matrix, filling in missing landmarks with NA
. The rows correspond to landmarks and the columns correspond to the number of landmark dimensions (2 for 2D landmarks, 3 for 3D landmarks, etc.). Each landmark matrix is appended as new columns onto the existing matrix. So, if three, 2D landmark files are input the resulting matrix would have six columns.
The landmark files are read by read.file()
and should thus conform to all requirements of read.file()
. Arguments for read.file()
can be passed through readLandmarksToList()
(e.g. header
, row.names
, etc.). All landmark matrices must have row names.
a landmark matrix
Aaron Olsen
readLandmarksToList
, readLandmarksToArray
, readCheckerboardsToArray
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD file <- paste0(fdir, "lm_2d_a3_v", 1:2, "_wna.txt") ## LOAD FILES INTO A MATRIX readLandmarksToMatrix(file=file, row.names=1) ## LOAD FILES INTO A MATRIX OMITTING NAS readLandmarksToMatrix(file=file, row.names=1, na.omit=TRUE)
## GET FILE DIRECTORY FOR PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET FILES TO LOAD file <- paste0(fdir, "lm_2d_a3_v", 1:2, "_wna.txt") ## LOAD FILES INTO A MATRIX readLandmarksToMatrix(file=file, row.names=1) ## LOAD FILES INTO A MATRIX OMITTING NAS readLandmarksToMatrix(file=file, row.names=1, na.omit=TRUE)
This function reads digitized shape and scaling data from a StereoMorph shape file or files into a list structure.
readShapes(file, fields=NULL)
readShapes(file, fields=NULL)
file |
A shape file, a vector of shape files or a folder containing shape files to be read. |
fields |
Objects to be returned from the shape file. If |
The digitizeImage
function makes it possible to save shape and scaling data into a single shape file. This shape file has an XML-like format that allows the readShapes()
function to read multiple object types (including vectors, matrices and lists) into a list structure using a generalized routine. All these objects are saved to a list as several elements. The particular elements in the output list will depend on which objects are present in the file. If the object is not present in the file, a call to that object will return NULL. The contents will also differ if multiple files are input. For instance, if one file is input landmarks.pixel
will be a matrix but if multiple files are input it will be an array.
The output of print()
on the entire output list is formatted for readability given the potentially large matrices contained within the list.
a list of class "shapes"
containing any number of the following elements:
image.name |
A vector of image names. |
image.id |
A vector of image IDs. |
scaling |
A vector of the scaling (real-world units per pixel) of the image. |
scaling.units |
A vector of the units of |
ruler.pixel |
A vector of the interval of the digitized ruler points in pixels. |
ruler.interval |
A vector of the interval of the digitized ruler points in real-world units. |
checkerboard.nx |
A vector of the number of internal corners of a checkerboard pattern along one dimension. |
checkerboard.ny |
A vector of the number of internal corners of a checkerboard pattern along the other dimension. |
square.pixel |
A vector of the best-fit checkerboard square size in pixels. |
square.size |
A vector of the best-fit checkerboard square size in real-world units. |
landmarks.pixel |
A matrix or array of landmark coordinates in pixels. |
landmarks.scaled |
A matrix or array of scaled landmark coordinates. |
ruler.points |
A matrix or array of ruler points in pixels. |
checker.pixel |
A matrix or array of checkerboard points in pixels. |
curves.control |
A list of Bezier curve control points in pixels. |
curves.pixel |
A list of Bezier curve points in pixels. |
curves.scaled |
A list of scaled Bezier curve points. |
If any of the above objects are absent from the shape file they will be NULL
.
Aaron Olsen
This function reconstructs and unifies landmarks and curves from multiple stereo sets. This function is a wrapper integrating dltReconstruct
, dltMatchCurvePoints
, and unifyLandmarks
.
reconstructStereoSets(shapes.2d, shapes.3d, cal.file, set.names = NULL, min.common = 3, unify = TRUE, reconstruct.curves = TRUE, even.spacing = NULL, print.progress = TRUE, verbose = FALSE, update.only = FALSE, min.direct.tangency = 25, min.fill.tangency = 10, epi.err.weight = 0, rec.err.weight = 1, curves.as.landmarks = FALSE, curve.name.width = 5)
reconstructStereoSets(shapes.2d, shapes.3d, cal.file, set.names = NULL, min.common = 3, unify = TRUE, reconstruct.curves = TRUE, even.spacing = NULL, print.progress = TRUE, verbose = FALSE, update.only = FALSE, min.direct.tangency = 25, min.fill.tangency = 10, epi.err.weight = 0, rec.err.weight = 1, curves.as.landmarks = FALSE, curve.name.width = 5)
shapes.2d |
file path to a folder containing 2D (digitized) shape files, separated by view into different folders. |
shapes.3d |
file path to a folder where the 3D shape files will be saved (if it does not already exist one will be created). |
cal.file |
file path to calibration file created by |
set.names |
vector of object or specimen names to be processed by the function. If |
min.common |
integer indicating the minimum number of common points required for unification of landmark sets. |
unify |
logical indicating whether to unify different aspects of the same object or specimen. If sets are to be unified, the filenames should end in '_a#' (e.g. '_a1', '_a2', etc.) to indicate different aspects of the same object. |
reconstruct.curves |
logical indicating whether to reconstruct curves. |
even.spacing |
specifies the number of evenly spaced points to be on each curve. This can be an integer (if the number of points for all curve(s) is the same), a list (in which the names of the list elements correspond to the curve names), or a .txt file containing a two column matrix of curve names and the number of points on each curve, separated by tabs and without quotes. |
print.progress |
logical indicating whether function processes should be printed to the console. |
verbose |
logical indicating whether |
update.only |
logical indicating whether function should only reconstruct sets for which the 2D data has been modified. If |
min.direct.tangency |
input parameter passed to |
min.fill.tangency |
input parameter passed to |
epi.err.weight |
input parameter passed to |
rec.err.weight |
input parameter passed to |
curves.as.landmarks |
logical indicating whether curve points should be saved as landmarks (will be added to any existing landmarks). |
curve.name.width |
integer indicating the width of numbers added to the curve name in generating curve-to-landmark names. If |
Please see StereoMorph tutorials for step-by-step tutorials on how to use StereoMorph for 2D or 3D shape data collection.
NULL
Aaron Olsen
calibrateCameras
,
digitizeImages
This function reflects missing bilateral landmarks across the plane of symmetry, optionally averaging left and right landmarks.
reflectMissingLandmarks(lm.matrix, left = '(_l|_left)([_]?[0-9]*$)', right = '(_r|_right)([_]?[0-9]*$)', left.remove = '\\2', right.remove = '\\2', left.replace = '_R\\2', right.replace = '_L\\2', average = FALSE) ## S3 method for class 'reflectMissingLandmarks' summary(object, ...)
reflectMissingLandmarks(lm.matrix, left = '(_l|_left)([_]?[0-9]*$)', right = '(_r|_right)([_]?[0-9]*$)', left.remove = '\\2', right.remove = '\\2', left.replace = '_R\\2', right.replace = '_L\\2', average = FALSE) ## S3 method for class 'reflectMissingLandmarks' summary(object, ...)
lm.matrix |
a 2D or 3D matrix with landmark names as row names. |
left |
a regular expression to identify left landmarks in the row names of |
right |
a regular expression to identify right landmarks in the row names of |
left.remove |
an expression for input to the |
right.remove |
an expression for input to the |
left.replace |
an expression for input to the |
right.replace |
an expression for input to the |
average |
a logical indicating whether bilateral landmarks should be averaged. |
object |
a list of class |
... |
further arguments passed to or from other methods. |
Currently, the function only accepts left/right designations by matching a regular expression to the row names of lm.matrix
. This is preferable since it allows for easier match up between bilateral landmarks. The default regular expression identifies left landmarks by a name ending in "_L", "_l", "_left" or "_LEFT", optionally followed by numbers. For example, "hamulus_left", "hamulus_L" and "zymgomatic_arch_l012" would all be identified as landmarks on the left side. Similarly, "hamulus_right", "hamulus_R" and "zymgomatic_arch_r012" would all be identified as landmarks on the right side. Landmarks not identified as left or right are assumed to fall on the midline.
In order to find corresponding left and right landmarks, the function requires the left.remove
and right.remove
arguments. The left.remove
and right.remove
arguments are passed to the base function gsub()
as the replacement
argument. This is used to generate a landmark name that is not side-specific. For example, "hamulus_left" and "zymgomatic_arch_l012" would become "hamulus" and "zymgomatic_arch012". These will be reverted to their original names at return.
If only a left or right landmark is present in lm.matrix
, reflectMissingLandmarks()
will create new a new row in lm.matrix
for the missing, contralateral landmark. Thus, the output matrix could be longer than the input matrix. The arguments left.replace
and right.replace
are used to create these new rownames by converting landmark names from left to right or vice versa. By default, the function replaces the existing side designation with "_L" and "_R". For instance, "hamulus_left" and "zymgomatic_arch_L012" would become "hamulus_R" and "zymgomatic_arch_R012", respectively. None of the names of existing landmarks will be modified. Users wanting a different left/right scheme can either change the left.replace
and right.replace
arguments or make sure that all the bilateral landmarks in lm.matrix
are represented by both a left and right landmark (missing values being NA
). In this case, left.replace
and right.replace
will be ignored and no new landmark names will be created.
Once corresponding right and left landmarks have been identified, the plane of object symmetry is found as described by Klingenberg et al. (2002). This includes creating two landmark sets, reflecting one set across the xy-plane, swapping left and right landmark names in one set and performing Procrustes alignment on the two sets. The user then has the option of averaging across the plane of symmetry. This will cause all bilateral landmarks to be mirror images across the midline plane and midline landmarks to lie directly in the midline plane. The input orientation of lm.matrix
is maintained. So if average
is FALSE
, landmarks that were not missing will be unchanged at output (the new landmarks having been filled in around them). If average
is TRUE
, the positions of the non-missing landmarks will have changed due to averaging but will only be shifted slightly from the original position.
reflectMissingLandmarks()
returns an alignment error vector. This is the error (distance) between a left or right landmark and its contralateral landmark (if present) when reflected across the midline plane. This is equivalent to the Procrustes alignment error.
Users with landmark names in alternative formats might find it easier to simply add '_L' and '_R' to the end of left and right landmark names, respectively, rather than re-specifying the regular expression arguments.
a list of class "reflectMissingLandmarks"
with the following elements:
lm.matrix |
a 2D or 3D matrix of landmarks with missing landmarks reflected. This matrix could be longer than the input landmark matrix. |
align.error |
a vector of the error (distance) between between a left or right landmark and its contralateral landmark (if present) when reflected across the plane of symmetry. |
This function was modified by A Olsen from the R function OSymm()
written by A Haber.
Annat Haber, Aaron Olsen
Klingenberg, C.P., Barluengua, M., Meyer, A. (2002) Shape analysis of symmetric structures: Quantifying variation among individuals and asymmetry. Evolution, 56 (10), 1909–1920.
readLandmarksToMatrix
, alignLandmarksToMidline
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET LANDMARKS file <- paste0(fdir, "lm_3d_unify.txt") ## LOAD FILES INTO A MATRIX lm.matrix <- readLandmarksToMatrix(file=file, row.names=1) ## ALIGN TO MIDLINE reflect_missing <- reflectMissingLandmarks(lm.matrix=lm.matrix, average=TRUE) ## PRINT SUMMARY OF ERRORS print(summary(reflect_missing))
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET LANDMARKS file <- paste0(fdir, "lm_3d_unify.txt") ## LOAD FILES INTO A MATRIX lm.matrix <- readLandmarksToMatrix(file=file, row.names=1) ## ALIGN TO MIDLINE reflect_missing <- reflectMissingLandmarks(lm.matrix=lm.matrix, average=TRUE) ## PRINT SUMMARY OF ERRORS print(summary(reflect_missing))
This function fits a 12-parameter image perspective model to imaged grid points and uses the model parameters to produce a grid with the same transformations but consisting of fewer points, effectively "resampling" the number of grid points. In this way, fewer points (but representing the same amount of information) can be used in more computationally intensive steps such as camera calibration. This function is called by dltCalibrateCameras
.
resampleGridImagePoints(pts, nx, rx, ry, fit.min.break=1, print.progress = FALSE)
resampleGridImagePoints(pts, nx, rx, ry, fit.min.break=1, print.progress = FALSE)
pts |
a matrix of grid points from an image, such as the internal corners of a checkerboard image. |
nx |
the number of points along the first dimension (e.g. this would be the number of points in each row if points in |
rx |
the re-sampled number of points along the first dimension (corresponding to |
ry |
the re-sampled number of points along the second dimension (e.g. if |
fit.min.break |
a minimum returned by |
print.progress |
whether the model fit error should be printed. Error is in the same units as |
a list with the following elements:
pts |
a matrix of resampled grid points. |
error |
the error (in the same units as |
Aaron Olsen
imagePlaneGridTransform
, readCheckerboardsToArray
, imagePlaneGridTransformError
,
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET GRID POINTS file <- paste0(fdir, "cal_a1_v2.txt") ## SET NUMBER OF GRID ROWS AND COLUMNS nx <- 21 ny <- 14 ## READ THE GRID POINTS INTO A MATRIX ## OUTPUT OF FUNCTION IS AN ARRAY SO WE TAKE THE FIRST ENTRY TO GET MATRIX coor.2d <- as.matrix(read.table(file)) ## RESAMPLE THE GRID WITH THE SAME NUMBER OF POINTS AS IN ORIGINAL coor_2d_same <- resampleGridImagePoints(pts=coor.2d, nx=nx, rx=21, ry=14, print.progress=TRUE) ## RESAMPLE THE GRID WITH A REDUCED NUMBER OF POINTS (4 X 4) coor_2d_red <- resampleGridImagePoints(pts=coor.2d, nx=nx, rx=4, ry=4, fit.min.break=1, print.progress=TRUE) ## PLOT THE ORIGINAL IMAGED POINTS plot(coor.2d) ## PLOT THE MODELED GRID POINTS WITHIN THE ORIGINAL POINTS ## THE MODEL GOODNESS-OF-FIT CAN BE EVALUATED VISUALLY points(coor_2d_same$pts, col='red', cex=0.75) ## PLOT THE REDUCED NUMBER OF GRID POINTS points(coor_2d_red$pts, col='green', lwd=2, cex=1.25) ## PLOT A HISTOGRAM OF THE FIT ERROR ## HERE UNITS ARE PIXELS - MOST POINTS ARE FIT WITHIN 2 PIXELS hist(coor_2d_same$error)
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## GET GRID POINTS file <- paste0(fdir, "cal_a1_v2.txt") ## SET NUMBER OF GRID ROWS AND COLUMNS nx <- 21 ny <- 14 ## READ THE GRID POINTS INTO A MATRIX ## OUTPUT OF FUNCTION IS AN ARRAY SO WE TAKE THE FIRST ENTRY TO GET MATRIX coor.2d <- as.matrix(read.table(file)) ## RESAMPLE THE GRID WITH THE SAME NUMBER OF POINTS AS IN ORIGINAL coor_2d_same <- resampleGridImagePoints(pts=coor.2d, nx=nx, rx=21, ry=14, print.progress=TRUE) ## RESAMPLE THE GRID WITH A REDUCED NUMBER OF POINTS (4 X 4) coor_2d_red <- resampleGridImagePoints(pts=coor.2d, nx=nx, rx=4, ry=4, fit.min.break=1, print.progress=TRUE) ## PLOT THE ORIGINAL IMAGED POINTS plot(coor.2d) ## PLOT THE MODELED GRID POINTS WITHIN THE ORIGINAL POINTS ## THE MODEL GOODNESS-OF-FIT CAN BE EVALUATED VISUALLY points(coor_2d_same$pts, col='red', cex=0.75) ## PLOT THE REDUCED NUMBER OF GRID POINTS points(coor_2d_red$pts, col='green', lwd=2, cex=1.25) ## PLOT A HISTOGRAM OF THE FIT ERROR ## HERE UNITS ARE PIXELS - MOST POINTS ARE FIT WITHIN 2 PIXELS hist(coor_2d_same$error)
Converts shape data in the TPS format into the StereoMorph shape file format, primarily for use with the StereoMorph digitizing application
TPSToShapes(tps.file, shapes.file, image.file, landmark.names, spec.names = c("IMAGE"), scaling.units = NULL, flip.y = TRUE)
TPSToShapes(tps.file, shapes.file, image.file, landmark.names, spec.names = c("IMAGE"), scaling.units = NULL, flip.y = TRUE)
tps.file |
A TPS file. |
shapes.file |
A folder where the shape files will be saved. If the TPS file contains landmark sets for more than one specimen, the landmarks for each specimen will be written to a separate shape file. |
image.file |
A folder containing images corresponding to each of the specimens in the TPS file. The image filenames must match the text in the |
landmark.names |
The names corresponding to the landmarks in the TPS file, in the same order in which they are listed in the TPS file. This can be either a vector of landmark names or a '.txt' file with each of the landmark names listed on a separate line. |
spec.names |
The label in the TPS file indicating the specimen name. |
scaling.units |
The scaling units for the TPS landmarks (e.g 'cm', 'mm'). |
flip.y |
A logical indicating whether the y-coordinates of the TPS landmarks should be flipped. |
TPS is a common file format used in morphometrics. This function reads landmarks from a TPS file and converts these into the StereoMorph shape file format. This function is intended for users who have previously collected shape data in the TPS format that they would like to import into StereoMorph. The resulting shape files can be opened with the StereoMorph digitizing application (see digitizeImages
).
In most cases, flip.y
should be TRUE
(the default). The StereoMorph digitizing application follows the opposite convention from TPS with regard to which part of the image corresponds to the 0 y-coordinate (i.e. top versus bottom). By flipping the y-coordinates the landmarks are properly rendered on top of the image in the digitizing application. Flipping is performed using the height of the corresponding image. For this reason it is necessary to include the image.file
parameter to determine the height of the corresponding image.
NULL
Aaron Olsen
## Not run: # Convert TPS file to a series of StereoMorph shape files TPSToShapes(tps.file='tps_file.TPS', shapes.file='Shapes', image.file='Images', landmark.names='landmarks.txt', scaling.units='mm') ## End(Not run)
## Not run: # Convert TPS file to a series of StereoMorph shape files TPSToShapes(tps.file='tps_file.TPS', shapes.file='Shapes', image.file='Images', landmark.names='landmarks.txt', scaling.units='mm') ## End(Not run)
This function rotates and translates a planar grid or grids according to specified transformation parameters. This function is called by dltCalibrateCameras
, to find the optimal transformation parameters for a set of arbitrarily oriented grid points that minimizes DLT calibration error. This function is also called by dltTestCalibration
to generate an ideal grid for accuracy testing.
transformPlanarCalibrationCoordinates(tpar, nx, ny, sx, sy = NULL)
transformPlanarCalibrationCoordinates(tpar, nx, ny, sx, sy = NULL)
tpar |
a vector of six transformation parameters per grid. The first three being rotational parameters (rotation about the z, y and x axes, respectively) and the second three being translational parameters (translation along the x, y and z axes, respectively). For more than one grid, these six values are concatenated as a vector. |
nx |
the number of points along the first dimension (e.g. this would be the number of points in each row if points are listed first by row). |
ny |
the number of points along the second dimension (e.g. this would be the number of points in each column if points are listed first by row). |
sx |
a scaling factor along the first dimension. |
sy |
a scaling factor along the second dimension. If the grid blocks are squares, this can be left as |
a matrix of transformed 3D grid coordinates
Aaron Olsen
dltCalibrateCameras
,
dltTransformationParameterRMSError
,
dltTestCalibration
This function aligns two or more landmark sets using shared points. Corresponding landmarks are identified by matching row names. The function selects a sequence of alignments that minimizes the step-wise alignment error.
unifyLandmarks(lm.array, min.common = dim(lm.array)[2], return.on.error = FALSE) ## S3 method for class 'unifyLandmarks' summary(object, print.tab = '', verbose = TRUE, ...)
unifyLandmarks(lm.array, min.common = dim(lm.array)[2], return.on.error = FALSE) ## S3 method for class 'unifyLandmarks' summary(object, print.tab = '', verbose = TRUE, ...)
lm.array |
an array of 2D or 3D landmark matrices. These can be read in from a file or files using |
min.common |
a minimum number of landmarks to use in the alignment. Must be greater than |
return.on.error |
Logical whether to return |
object |
a list of class |
print.tab |
Tabs preceding lines printed to console. |
verbose |
Logical whether to print full error report. |
... |
further arguments passed to or from other methods. |
The input lm.array
should be an array of 2D or 3D landmark matrices with row names, such as created by readLandmarksToArray
. The first two dimensions of lm.array
correspond to the rows and columns of each matrix, respectively. The last dimension of lm.array
corresponds to each separate landmark matrix.
unifyLandmarks()
first aligns all pair combinations of landmark sets that share the minimum number of points specified by min.common
. The two sets that align with the lowest root-mean-square (RMS) error are aligned and the mean positions of all points saved. If there are additional landmark sets, unifyLandmarks()
aligns each of these with the combined matrix, again identifying the set that aligns with the least RMS error. The alignment with the least error is saved as the new combined landmark matrix. This is repeated for each remaining landmark set, sequentially aligning remaining landmark sets to the combined landmark matrix.
To align two 2D landmark sets, the sets must share at least two landmarks and to align two 3D landmark sets, the sets must share at least three landmarks. These are the default minimum number of points for alignment. A greater number of common points can be specified using the min.common
parameter. Additionally, in the 3D case, these landmarks must not be collinear. If lm.array
contains more than two landmark matrices, it is not necessarily required that each landmark set share these minimum number of points with every other landmark set. For example, it may be that two landmark sets do not each separately share the minimum number of landmarks required for alignment with a third landmark set. But if these two landmark sets are combined, they may then share the required number of landmarks with the third set. During each alignment step, unifyLandmarks()
skips pairs of matrices that do not share the required number of landmarks. As long as there is some combination of alignments that provide a sufficient number of shared landmarks, all landmark sets can be combined into a single matrix.
If an array consisting of only one landmark matrix is input, the matrix is returned without an alignment operation.
a list of class "unifyLandmarks"
with the following elements:
lm.matrix |
a 2D or 3D landmark matrix. |
unify.seq |
a vector of the order in which landmark sets were aligned. |
unify.error |
a matrix of the alignment error for each shared landmark for each alignment (the number of sets minus one). |
unify.rmse |
a vector of the root-mean-square error of each alignment (the number of sets minus one). |
This function was modified by A Olsen from the R function unifyVD()
written by A Haber.
Annat Haber, Aaron Olsen
Rohlf, F.J. (1990) "Chapter 10. Rotational fit (Procrustes) Methods." Proceedings of the Michigan Morphometrics Workshop. Ed. F. James Rohlf and Fred L. Bookstein. The University of Michigan Museum of Zoology, 1990. 227–236. Info page at lib.umich.edu
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET LANDMARK FILES file <- paste0(fdir, "lm_3d_even_a", 1:3, ".txt") ## READ LANDMARKS INTO ARRAY lm.array <- readLandmarksToArray(file, row.names=1) ## UNIFY LANDMARKS unify_lm <- unifyLandmarks(lm.array) ## PRINT UNIFICATION SUMMARY print(summary(unify_lm))
## FIND THE FILE DIRECTORY FOR EXTRA R PACKAGE FILES fdir <- paste0(path.package("StereoMorph"), "/extdata/") ## SET LANDMARK FILES file <- paste0(fdir, "lm_3d_even_a", 1:3, ".txt") ## READ LANDMARKS INTO ARRAY lm.array <- readLandmarksToArray(file, row.names=1) ## UNIFY LANDMARKS unify_lm <- unifyLandmarks(lm.array) ## PRINT UNIFICATION SUMMARY print(summary(unify_lm))
Reads landmarks from a StereoMorph shape file and writes them in the TPS file format
writeLMToTPS(shapes.file, tps.file, in.pixels = TRUE, flip.y = TRUE, flip.x = FALSE)
writeLMToTPS(shapes.file, tps.file, in.pixels = TRUE, flip.y = TRUE, flip.x = FALSE)
shapes.file |
A single shape file or folder containing multiple shape files. If this is a folder containing multiple files then all of the landmarks from each file will be written into a single TPS file as separate specimens. |
tps.file |
A TPS file where landmarks will be saved. |
in.pixels |
A logical indicating whether function should write pixel or scaled landmark coordinates to the TPS file. |
flip.y |
A logical indicating whether to flip the y-coordinates of the landmarks. This may be necessary depending on how another program renders the image. |
flip.x |
A logical indicating whether to flip the x-coordinates of the landmarks. This may be necessary depending on how another program renders the image. |
TPS is a common file format used in morphometrics. This function converts landmarks from the StereoMorph shape file format into the TPS format. This is intended for users who would like to input landmark data collected using StereoMorph into a program that reads TPS files.
NULL
Aaron Olsen
## Not run: # Get the path to package example files fdir <- paste0(path.package("StereoMorph"), "/extdata/") # Write 2D landmark pixel coordinates from a single shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_2D/mug_003.txt'), 'Mug_2D.tps') # Write 3D landmark coordinates from a single shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_3D/bubo_virginianus_FMNH488595.txt'), 'Owl_3D.tps') # Write 2D landmark pixel coordinates from multiple shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_2D'), 'Shapes_2D.tps') # Write 3D landmark coordinates from multiple shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_3D'), 'Shapes_3D.tps', in.pixels=FALSE) ## End(Not run)
## Not run: # Get the path to package example files fdir <- paste0(path.package("StereoMorph"), "/extdata/") # Write 2D landmark pixel coordinates from a single shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_2D/mug_003.txt'), 'Mug_2D.tps') # Write 3D landmark coordinates from a single shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_3D/bubo_virginianus_FMNH488595.txt'), 'Owl_3D.tps') # Write 2D landmark pixel coordinates from multiple shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_2D'), 'Shapes_2D.tps') # Write 3D landmark coordinates from multiple shape file to TPS writeLMToTPS(paste0(fdir, 'Shapes_3D'), 'Shapes_3D.tps', in.pixels=FALSE) ## End(Not run)