image-js
Version:
Image processing and manipulation in JavaScript
78 lines • 3.49 kB
JavaScript
import { match } from 'ts-pattern';
import { getCirclePoints, getCompassPoints, } from '../../utils/geometry/getCirclePoints.js';
import { getIndex } from '../../utils/getIndex.js';
import { surroundingPixels } from '../../utils/surroundingPixels.js';
import checkProcessable from '../../utils/validators/checkProcessable.js';
import { getFastScore } from './getFastScore.js';
import { getHarrisScore } from './getHarrisScore.js';
import { getShiTomasiScore } from "./getShiTomasiScore.js";
import { isFastKeypoint } from './isFastKeypoint.js';
/**
* Find the features in a GREY image according to the FAST (Features from Accelerated Segment Test) algorithm.
* Based on the paper Machine Learning for High-Speed Corner Detection.
* DOI: https://doi.org/10.1007/11744023_34.
* @param image - The image to process.
* @param options - Get FAST keypoints options.
* @returns The FAST keypoints.
*/
export function getFastKeypoints(image, options = {}) {
const { fastRadius = 3, scoreAlgorithm = 'FAST', scoreOptions } = options;
const circlePoints = getCirclePoints(fastRadius);
const compassPoints = getCompassPoints(fastRadius);
const { maxNbFeatures = 500, nbContiguousPixels = (3 / 4) * circlePoints.length, threshold = 20, nonMaxSuppression = true, } = options;
checkProcessable(image, {
channels: [1],
alpha: false,
});
function harrisScore(image, corner) {
return getHarrisScore(image, corner, scoreOptions);
}
function fastScore(image, corner) {
return getFastScore(image, corner, threshold, circlePoints);
}
function tomasiScore(image, corner) {
return getShiTomasiScore(image, corner, scoreOptions);
}
const getScore = match(scoreAlgorithm)
.with('HARRIS', () => harrisScore)
.with('FAST', () => fastScore)
.with('TOMASI', () => tomasiScore)
.exhaustive();
const allKeypoints = [];
const scoreArray = new Float64Array(image.size).fill(Number.NEGATIVE_INFINITY);
for (let row = 0; row < image.height; row++) {
for (let column = 0; column < image.width; column++) {
const corner = { row, column };
if (isFastKeypoint(corner, image, circlePoints, compassPoints, {
nbContiguousPixels,
threshold,
})) {
const score = getScore(image, corner);
scoreArray[getIndex(corner.column, corner.row, image, 0)] = score;
allKeypoints.push({ origin: corner, score });
}
}
}
let keypoints = [];
if (!nonMaxSuppression) {
keypoints = allKeypoints;
}
else {
// Non-Maximal Suppression
for (const keypoint of allKeypoints) {
const currentScore = scoreArray[getIndex(keypoint.origin.column, keypoint.origin.row, image, 0)];
for (let i = 0; i < surroundingPixels.length; i++) {
const neighbour = surroundingPixels[i];
const neighbourScore = scoreArray[getIndex(keypoint.origin.column + neighbour.column, keypoint.origin.row + neighbour.row, image, 0)];
if (neighbourScore > currentScore)
break;
if (i === surroundingPixels.length - 1) {
keypoints.push(keypoint);
}
}
}
}
keypoints.sort((a, b) => b.score - a.score);
return keypoints.slice(0, maxNbFeatures);
}
//# sourceMappingURL=getFastKeypoints.js.map