import { xMedian } from 'ml-spectra-processing'; import { Image } from '../Image.js'; import { getDefaultColor } from '../utils/getDefaultColor.ts'; import type { BorderType } from '../utils/interpolateBorder.js'; import { getBorderInterpolation } from '../utils/interpolateBorder.js'; import checkProcessable from '../utils/validators/checkProcessable.js'; export interface MedianFilterOptions { /** * Type of border algorithm to interpolate from. * @default `'reflect101'` */ borderType?: BorderType; /** * Value of border. */ borderValue?: number | number[]; /** * The radius of the cell to extract median value from. Must be odd. * @default `3` */ cellSize?: number; } /** * Calculate a new image that replaces all pixel values by the median of neighbouring pixels. * @param image - Image to be filtered. * @param options - MedianFilterOptions * @returns Image after median filter. */ export function medianFilter(image: Image, options: MedianFilterOptions = {}) { const { cellSize = 3, borderType = 'reflect101', borderValue = getDefaultColor(image), } = options; checkProcessable(image, { bitDepth: [8, 16], }); if (cellSize < 1) { throw new RangeError( `Invalid property "cellSize". Must be greater than 0. Received ${cellSize}.`, ); } if (cellSize % 2 === 0) { throw new RangeError( `Invalid property "cellSize". Must be an odd number. Received ${cellSize}.`, ); } const interpolateBorder = getBorderInterpolation(borderType, borderValue); const newImage = Image.createFrom(image); const size = cellSize ** 2; const cellValues = new Uint16Array(size); const halfCellSize = (cellSize - 1) / 2; for (let channel = 0; channel < image.channels; channel++) { for (let row = 0; row < image.height; row++) { for (let column = 0; column < image.width; column++) { let n = 0; for (let cellRow = -halfCellSize; cellRow <= halfCellSize; cellRow++) { for ( let cellColumn = -halfCellSize; cellColumn <= halfCellSize; cellColumn++ ) { cellValues[n++] = interpolateBorder( column + cellColumn, row + cellRow, channel, image, ); } } newImage.setValue(column, row, channel, xMedian(cellValues)); } } } return newImage; }