ts-scikit
Version:
A scientific toolkit written in Typescript
317 lines • 10.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EigenTensors3 = void 0;
const eigen_solver_1 = require("./eigen-solver");
/**
* An array of eigen-decomposition of tensors for 3D image processing.
* Each tensor is a symmetric positive semi-definite 3x3 matrix.
* <pre>
* | a11 a12 a13 |
* A = | a12 a22 a23 |
* | a13 a23 a33 |
* </pre>
* Such tensors can be used to parametrize anisotropic image processing.
* <p>
* The eigen-decomposition of the matrix A is
* <pre>
* A = au * u * u' + av * v * v' + aw * w * w'
* = (au - av) * u * u' + (aw - av) * w * w' + av * I
* </pre>
* where, u, v, and w are orthogonal unit eigenvectors of A. (The notation
* u' denotes the transpose of u.) The outer products of eigenvectors are
* scaled by the non-negative eigenvalues au, av and aw. The second
* equation exploits the identity u * u' + v * v' + w * w' = I, and makes
* apparent the redundancy of the vector v.
* <p>
* Only the 1st and 2nd components of the eigenvectors u and w are stored.
* Except for a sign, the 3rd components may be computed from the 1st and
* 2nd. Because the tensors are independent of the choice of the sign, the
* eigenvectors u and w are stored with an implied non-negative 3rd
* component.
* <p>
* Storage may be further reduced by compression, whereby eigenvalues and
* eigenvectors are quantized.
*/
class EigenTensors3 {
/**
* Constructs tensors for specified array dimensions.
* <p>
* All eigenvalues and eigenvectors u and w are not set and are initially
* zero.
* @param n1 number of tensors in 1st dimension.
* @param n2 number of tensors in 2nd dimension.
* @param n3 number of tensors in 3rd dimension.
*/
constructor(n1, n2, n3) {
this._n1 = n1;
this._n2 = n2;
this._n3 = n3;
}
static c3(c1, c2) {
const c3s = 1.0 - c1 * c1 - c2 * c2;
return (c3s > 0) ? Math.sqrt(c3s) : 0.0;
}
/**
* Gets tensor elements for specified indices.
* <p>
* Note: If passing in an array, its values are edited in-place and
* nothing is returned.
* @param i1 index for 1st dimension.
* @param i2 index for 2nd dimension.
* @param i3 index for 3rd dimension.
* @param a the array { a11, a12, a13, a22, a23, a33 } of tensor elements.
* @returns the array { a11, a12, a13, a22, a23, a33 } of tensor elements.
*/
getTensor(i1, i2, i3, a) {
const asum = this._as[i3][i2][i1];
let au, av, aw, u1, u2, u3, w1, w2, w3;
au = this._au[i3][i2][i1];
aw = this._aw[i3][i2][i1];
u1 = this._u1[i3][i2][i1];
u2 = this._u2[i3][i2][i1];
u3 = EigenTensors3.c3(u1, u2);
w1 = this._w1[i3][i2][i1];
w2 = this._w2[i3][i2][i1];
w3 = EigenTensors3.c3(w1, w2);
av = asum - au - aw;
au -= av;
aw -= av;
const a11 = au * u1 * u1 + aw * w1 * w1 + av;
const a12 = au * u1 * u2 + aw * w1 * w1;
const a13 = au * u1 * u3 + aw * w1 * w3;
const a22 = au * u2 * u2 + aw * w2 * w2 + av;
const a23 = au * u2 * u3 + aw * w2 * w3;
const a33 = au * u3 * u3 + aw * w3 * w3 + av;
if (a) {
a[0] = a11;
a[1] = a12;
a[2] = a13;
a[3] = a22;
a[4] = a23;
a[5] = a33;
}
else {
return [a11, a12, a13, a22, a23, a33];
}
}
/**
* Sets the eigenvalues for the tensor with specified indices.
* @param i1 index for the 1st dimension.
* @param i2 index for the 2nd dimension.
* @param i3 index for the 3rd dimension.
* @param au eigenvalue au.
* @param av eigenvalue av.
* @param aw eigenvalue aw.
*/
setEigenvalues(i1, i2, i3, au, av, aw) {
this._au[i3][i2][i1] = au;
this._aw[i3][i2][i1] = aw;
this._as[i3][i2][i1] = au + av + aw;
}
/**
* Gets eigenvalues for the tensor with specified indices.
* <p>
* Note: If passing in an array, its values are edited in-place and
* nothing is returned.
* @param i1 index for 1st dimension.
* @param i2 index for 2nd dimension.
* @param i3 index for 3rd dimension.
* @param a the array { au, av, aw } of eigenvalues.
* @returns the array { au, av, aw } of eigenvalues.
*/
getEigenvalues(i1, i2, i3, a) {
const asum = this._as[i3][i2][i1];
let au, aw;
au = this._au[i3][i2][i1];
aw = this._aw[i3][i2][i1];
if (a) {
a[0] = au;
a[1] = asum - au - aw;
a[2] = aw;
}
else {
return [
au,
asum - au - aw,
aw
];
}
}
/**
* Gets eigenvalues for all tensors.
* @param au array of eigenvalues au.
* @param av array of eigenvalues av.
* @param aw array of eigenvalues aw.
*/
getAllEigenvalues(au, av, aw) {
const auvw = new Array(3);
for (let i3 = 0; i3 < this._n3; ++i3) {
for (let i2 = 0; i2 < this._n2; ++i2) {
for (let i1 = 0; i1 < this._n1; ++i1) {
this.getEigenvalues(i1, i2, i3, auvw);
au[i3][i2][i1] = auvw[0];
av[i3][i2][i1] = auvw[1];
aw[i3][i2][i1] = auvw[2];
}
}
}
}
/**
* Sets the eigenvector u for the tensor with specified indices.
* <p>
* The specified vector is assumed to have length one. If the 3rd
* component is negative, this method stores the negative of the
* specified vector, so that the 3rd component is positive.
* @param i1 index for 1st dimension.
* @param i2 index for 2nd dimension.
* @param i3 index for 3rd dimension.
* @param u1 1st component of u.
* @param u2 2nd component of u.
* @param u3 3rd component of u.
*/
setEigenvectorU(i1, i2, i3, u1, u2, u3) {
if (u3 < 0.0) {
u1 = -u1;
u2 = -u2;
u3 = -u3;
}
this._u1[i3][i2][i1] = u1;
this._u2[i3][i2][i1] = u2;
}
/**
* Gets the eigenvector u for the tensor with specified indices.
* <p>
* Note: If passing in an array, its values are edited in-place and
* nothing is returned.
* @param i1 index for 1st dimension.
* @param i2 index for 2nd dimension.
* @param i3 index for 3rd dimension.
* @param u array { u1, u2, u3 } of eigenvector components.
*/
getEigenvectorU(i1, i2, i3, u) {
let u0, u1, u2;
u0 = this._u1[i3][i2][i1];
u1 = this._u2[i3][i2][i1];
u2 = EigenTensors3.c3(u0, u1);
if (!u) {
return [u0, u1, u2];
}
u[0] = u0;
u[1] = u1;
u[2] = u2;
}
/**
* Sets the eigenvector w for the tensor with specified indices.
* <p>
* The specified vector is assumed to have length one. If the 3rd
* component is negative, this method stores the negative of the
* specified vector, so that the 3rd component is positive.
* @param i1 index for 1st dimension.
* @param i2 index for 2nd dimension.
* @param i3 index for 3rd dimension.
* @param w1 1st component of w.
* @param w2 2nd component of w.
* @param w3 3rd component of w.
*/
setEigenvectorW(i1, i2, i3, w1, w2, w3) {
if (w3 < 0.0) {
w1 = -w1;
w2 = -w2;
w3 = -w3;
}
this._w1[i3][i2][i1] = w1;
this._w2[i3][i2][i1] = w2;
}
/**
* Gets the eigenvector w for the tensor with specified indices.
* <p>
* Note: If passing in an array, its values are edited in-place and
* nothing is returned.
* @param i1 index for 1st dimension.
* @param i2 index for 2nd dimension.
* @param i3 index for 3rd dimension.
* @param w array { w1, w2, w3 } of eigenvector components.
*/
getEigenvectorW(i1, i2, i3, w) {
let w0, w1, w2;
w0 = this._w1[i3][i2][i1];
w1 = this._w2[i3][i2][i1];
w2 = EigenTensors3.c3(w0, w1);
if (!w) {
return [w0, w1, w2];
}
w[0] = w0;
w[1] = w1;
w[2] = w2;
}
/**
* Gets the eigenvector v for the tensor with specified indices.
* <p>
* Note: If passing in an array, its values are edited in-place and
* nothing is returned.
* @param i1 index for 1st dimension.
* @param i2 index for 2nd dimension.
* @param i3 index for 3rd dimension.
* @param v array { v1, v2, v3 } of eigenvector components.
*/
getEigenvectorV(i1, i2, i3, v) {
const u = this.getEigenvectorU(i1, i2, i3);
const w = this.getEigenvectorW(i1, i2, i3);
// v = w cross u
const v0 = w[1] * u[2] - w[2] * u[1];
const v1 = w[2] * u[0] - w[0] * u[2];
const v2 = w[0] * u[1] - w[1] * u[0];
if (!v) {
return [v0, v1, v2];
}
v[0] = v0;
v[1] = v1;
v[2] = v2;
}
/**
* Sets tensor elements for specified indices.
* <p>
* This method first computes an eigen-decomposition of the specified
* tensor, and then stores the computed eigenvectors and eigenvalues.
* The eigenvalues are ordered such that au >= av >= aw >= 0.
*/
setTensor(i1, i2, i3, a11, a12, a13, a22, a23, a33) {
if (a11 instanceof Array) {
a12 = a11[1];
a13 = a11[2];
a22 = a11[3];
a23 = a11[4];
a33 = a11[5];
a11 = a11[0];
}
const aa = [
[a11, a12, a13],
[a12, a22, a23],
[a13, a23, a33]
];
const vv = new Array(3);
for (let i = 0; i < 3; ++i) {
vv[i] = new Array(3);
}
const ev = new Array(3);
eigen_solver_1.EigenSolver.SolveSymmetric3x3(aa, vv, ev, true);
const u = vv[0];
const w = vv[2];
const u1 = u[0];
const u2 = u[1];
const u3 = u[2];
const w1 = w[0];
const w2 = w[1];
const w3 = w[2];
const au = (ev[0] < 0.0) ? 0.0 : ev[0];
const av = (ev[1] < 0.0) ? 0.0 : ev[1];
const aw = (ev[2] < 0.0) ? 0.0 : ev[2];
this.setEigenvectorU(i1, i2, i3, u1, u2, u3);
this.setEigenvectorW(i1, i2, i3, w1, w2, w3);
this.setEigenvalues(i1, i2, i3, au, av, aw);
}
}
exports.EigenTensors3 = EigenTensors3;
EigenTensors3.AS_SET = Number.MAX_SAFE_INTEGER;
EigenTensors3.AS_GET = 1.0 / EigenTensors3.AS_SET;
//# sourceMappingURL=eigen-tensors3.js.map