import { match } from 'ts-pattern'; import type { BitDepth, Image } from '../Image.js'; import { PREWITT_X, PREWITT_Y, SCHARR_X, SCHARR_Y, SOBEL_X, SOBEL_Y, } from '../utils/constants/kernels.js'; import type { BorderType } from '../utils/interpolateBorder.js'; export const DerivativeFilter = { SOBEL: 'sobel', SCHARR: 'scharr', PREWITT: 'prewitt', // todo: handle even sized kernels to implement Roberts' filter // for 2x2 matrices, the current pixel corresponds to the top-left // ROBERTS = 'roberts', } as const; export type DerivativeFilter = (typeof DerivativeFilter)[keyof typeof DerivativeFilter]; export interface DerivativeFilterOptions { /** * Algorithm to use for the derivative filter. * @default `SOBEL` */ filter?: DerivativeFilter; /** * Specify how the borders should be handled. * @default `'replicate'` */ borderType?: BorderType; /** * Value of the border if BorderType is 'constant'. * @default `0` */ borderValue?: number; /** * Specify the bit depth of the resulting image. * @default `image.bitDepth` */ bitDepth?: BitDepth; } /** * Apply a derivative filter to an image. * @param image - Image to process. * @param options - Derivative filter options. * @returns The processed image. */ export function derivativeFilter( image: Image, options: DerivativeFilterOptions = {}, ): Image { const { filter = 'sobel' } = options; const kernels = match< DerivativeFilter, { kernelX: number[][]; kernelY: number[][] } >(filter) .with('sobel', () => ({ kernelX: SOBEL_X, kernelY: SOBEL_Y })) .with('scharr', () => ({ kernelX: SCHARR_X, kernelY: SCHARR_Y })) .with('prewitt', () => ({ kernelX: PREWITT_X, kernelY: PREWITT_Y })) .exhaustive(); return image.gradientFilter({ ...kernels, ...options }); }