image-js
Version:
Image processing and manipulation in JavaScript
96 lines • 3.93 kB
JavaScript
import { EigenvalueDecomposition } from 'ml-matrix';
import { xVariance, xyCovariance } from 'ml-spectra-processing';
import { getAngle } from '../../maskAnalysis/utils/getAngle.js';
import { toDegrees } from '../../utils/geometry/angles.js';
import { assert } from '../../utils/validators/assert.js';
/**
* Calculates ellipse on around ROI.
* @param roi - Region of interest.
* @returns Ellipse.
*/
export function getEllipse(roi) {
const xCenter = roi.centroid.column;
const yCenter = roi.centroid.row;
const xCentered = roi.relativePoints.map((point) => point.column - xCenter);
const yCentered = roi.relativePoints.map((point) => point.row - yCenter);
const centeredXVariance = xVariance(xCentered, { unbiased: false });
const centeredYVariance = xVariance(yCentered, { unbiased: false });
const centeredCovariance = xyCovariance({
x: xCentered,
y: yCentered,
}, { unbiased: false });
//spectral decomposition of the sample covariance matrix
const sampleCovarianceMatrix = [
[centeredXVariance, centeredCovariance],
[centeredCovariance, centeredYVariance],
];
const e = new EigenvalueDecomposition(sampleCovarianceMatrix);
const eigenvalues = e.realEigenvalues;
const vectors = e.eigenvectorMatrix;
assert(eigenvalues[0] <= eigenvalues[1]);
let radiusMajor = Math.sqrt(eigenvalues[1]);
let radiusMinor = Math.sqrt(eigenvalues[0]);
const vectorMajor = vectors.getColumn(1);
const vectorMinor = vectors.getColumn(0);
let majorAxisPoint1 = {
column: xCenter + radiusMajor * vectorMajor[0],
row: yCenter + radiusMajor * vectorMajor[1],
};
let majorAxisPoint2 = {
column: xCenter - radiusMajor * vectorMajor[0],
row: yCenter - radiusMajor * vectorMajor[1],
};
let minorAxisPoint1 = {
column: xCenter + radiusMinor * vectorMinor[0],
row: yCenter + radiusMinor * vectorMinor[1],
};
let minorAxisPoint2 = {
column: xCenter - radiusMinor * vectorMinor[0],
row: yCenter - radiusMinor * vectorMinor[1],
};
let majorLength = Math.hypot(majorAxisPoint1.column - majorAxisPoint2.column, majorAxisPoint1.row - majorAxisPoint2.row);
let minorLength = Math.hypot(minorAxisPoint1.column - majorAxisPoint2.column, minorAxisPoint1.row - minorAxisPoint2.row);
let ellipseSurface = (((minorLength / 2) * majorLength) / 2) * Math.PI;
if (ellipseSurface !== roi.surface) {
const scaleFactor = Math.sqrt(roi.surface / ellipseSurface);
radiusMajor *= scaleFactor;
radiusMinor *= scaleFactor;
majorAxisPoint1 = {
column: xCenter + radiusMajor * vectorMajor[0],
row: yCenter + radiusMajor * vectorMajor[1],
};
majorAxisPoint2 = {
column: xCenter - radiusMajor * vectorMajor[0],
row: yCenter - radiusMajor * vectorMajor[1],
};
minorAxisPoint1 = {
column: xCenter + radiusMinor * vectorMinor[0],
row: yCenter + radiusMinor * vectorMinor[1],
};
minorAxisPoint2 = {
column: xCenter - radiusMinor * vectorMinor[0],
row: yCenter - radiusMinor * vectorMinor[1],
};
majorLength *= scaleFactor;
minorLength *= scaleFactor;
ellipseSurface *= scaleFactor ** 2;
}
return {
center: {
column: xCenter,
row: yCenter,
},
majorAxis: {
points: [majorAxisPoint1, majorAxisPoint2],
length: majorLength,
angle: toDegrees(getAngle(majorAxisPoint1, majorAxisPoint2)),
},
minorAxis: {
points: [minorAxisPoint1, minorAxisPoint2],
length: minorLength,
angle: toDegrees(getAngle(minorAxisPoint1, minorAxisPoint2)),
},
surface: ellipseSurface,
};
}
//# sourceMappingURL=getEllipse.js.map