image-js
Version:
Image processing and manipulation in JavaScript
75 lines • 3.27 kB
JavaScript
/**
* Find border points that are farthest from its convex hull lines. Inspired by openCV's convexityDefects.
* @param borderPoints - Mask's border points.
* @param hullPoints - Mask's convex hull points.
* @param options - Convexity defects options.
* @returns Array of convexity defects.
*/
export function getConvexityDefects(borderPoints, hullPoints, options = {}) {
const { depthThreshold = 0 } = options;
const defects = [];
if (hullPoints.length === 0) {
throw new RangeError('No hull points were defined for convexity defects detection.');
}
if (borderPoints.length === 0) {
throw new RangeError('No border points were defined for convexity defects detection.');
}
const lastHullPoint = hullPoints.at(-1);
let currBorderIndex = borderPoints.findIndex((bp) => limitReached(bp, lastHullPoint));
if (currBorderIndex === -1) {
throw new RangeError('Could not find a border point matching the convex hull endpoint.');
}
for (let i = hullPoints.length - 1; i >= 0; i--) {
const currHullPoint = hullPoints.at(i);
const nextHullPoint = hullPoints.at(i - 1);
if (!currHullPoint || !nextHullPoint)
continue;
const v0x = nextHullPoint.column - currHullPoint.column;
const v0y = nextHullPoint.row - currHullPoint.row;
const edgeLenSq = v0x * v0x + v0y * v0y;
let maxDepth = 0;
let maxDepthIndex = -1;
const segmentStart = currBorderIndex;
let reached = false;
for (let j = 0; j < borderPoints.length; j++) {
const checkIndex = (segmentStart + j) % borderPoints.length;
const bp = borderPoints[checkIndex];
if (limitReached(bp, nextHullPoint)) {
currBorderIndex = (checkIndex + 1) % borderPoints.length;
reached = true;
break;
}
const v1x = bp.column - currHullPoint.column;
const v1y = bp.row - currHullPoint.row;
const t = Math.max(0, Math.min(1, (v1x * v0x + v1y * v0y) / edgeLenSq));
const closestX = currHullPoint.column + t * v0x;
const closestY = currHullPoint.row + t * v0y;
const depth = Math.hypot(bp.column - closestX, bp.row - closestY);
if (depth > maxDepth) {
maxDepth = depth;
maxDepthIndex = checkIndex;
}
}
if (!reached) {
throw new RangeError('Could not reach the next hull point while scanning border points; hull and border may be inconsistent.');
}
if (maxDepth > depthThreshold) {
const defectPoint = borderPoints[maxDepthIndex];
if (defectPoint)
defects.push(defectPoint);
}
}
return defects;
}
/**
* Checks if borderPoint reached convex hull's point.
* @param borderPoint - Border point.
* @param hullPoint - Endpoint of convex hull segment.
* @returns whether endpoint is reached.
*/
function limitReached(borderPoint, hullPoint) {
const tolerance = 1;
return (Math.abs(borderPoint.column - hullPoint.column) <= tolerance &&
Math.abs(borderPoint.row - hullPoint.row) <= tolerance);
}
//# sourceMappingURL=getConvexityDefects.js.map