UNPKG

image-js

Version:

Image processing and manipulation in JavaScript

91 lines 3.88 kB
import PriorityQueue from 'js-priority-queue'; import { getExtrema } from '../compute/index.js'; import checkProcessable from '../utils/validators/checkProcessable.js'; import { RoiMapManager } from './RoiMapManager.js'; /** * This method allows to create a ROIMap using the water shed algorithm. By default this algorithm * will fill the holes and therefore the lowest value of the image (black zones). * If no points are given, the function will look for all the minimal points. * If no mask is given the algorithm will completely fill the image. * Please take care about the value that has be in the mask ! In order to be coherent with the expected mask, * meaning that if it is a dark zone, the mask will be dark the normal behavior to fill a zone * is that the mask pixel is clear (value of 0) ! * If you are looking for 'maxima' the image must be inverted before applying the algorithm * @param image - Image that the filter will be applied to. * @param options - WaterShedOptions * @returns RoiMapManager */ export function waterShed(image, options) { let { points } = options; const { mask, threshold = 1 } = options; const currentImage = image; checkProcessable(image, { bitDepth: [8, 16], components: 1, }); const fillMaxValue = threshold * image.maxValue; // WaterShed is done from points in the image. We can either specify those points in options, // or it is gonna take the minimum locals of the image by default. if (!points) { points = getExtrema(image, { kind: 'minimum', mask, }); } const maskExpectedValue = 0; const data = new Int32Array(currentImage.size); const width = currentImage.width; const height = currentImage.height; const toProcess = new PriorityQueue({ comparator: (a, b) => a.intensity - b.intensity, strategy: PriorityQueue.BinaryHeapStrategy, }); for (let i = 0; i < points.length; i++) { const index = points[i].column + points[i].row * width; data[index] = -i - 1; const intensity = currentImage.getValueByIndex(index, 0); if (intensity <= fillMaxValue) { toProcess.queue({ column: points[i].column, row: points[i].row, intensity, }); } } const dxs = [1, 0, -1, 0, 1, 1, -1, -1]; const dys = [0, 1, 0, -1, 1, -1, 1, -1]; // Then we iterate through each points while (toProcess.length > 0) { const currentPoint = toProcess.dequeue(); const currentValueIndex = currentPoint.column + currentPoint.row * width; for (let dir = 0; dir < 4; dir++) { const newX = currentPoint.column + dxs[dir]; const newY = currentPoint.row + dys[dir]; if (newX >= 0 && newY >= 0 && newX < width && newY < height) { const currentNeighbourIndex = newX + newY * width; if (!mask || mask.getBitByIndex(currentNeighbourIndex) === maskExpectedValue) { const intensity = currentImage.getValueByIndex(currentNeighbourIndex, 0); if (intensity <= fillMaxValue && data[currentNeighbourIndex] === 0) { data[currentNeighbourIndex] = data[currentValueIndex]; toProcess.queue({ column: currentPoint.column + dxs[dir], row: currentPoint.row + dys[dir], intensity, }); } } } } } const nbNegative = points.length; const nbPositive = 0; return new RoiMapManager({ data, nbPositive, nbNegative, width: image.width, height: image.height, }); } //# sourceMappingURL=waterShed.js.map