image-js
Version:
Image processing and manipulation in JavaScript
207 lines (191 loc) • 5.02 kB
text/typescript
import { match } from 'ts-pattern';
import type { Image } from '../Image.js';
import type { BorderInterpolationFunction } from './utils.types.js';
export const BorderType = {
CONSTANT: 'constant',
REPLICATE: 'replicate',
REFLECT: 'reflect',
WRAP: 'wrap',
REFLECT_101: 'reflect101',
} as const;
export type BorderType = (typeof BorderType)[keyof typeof BorderType];
/**
* Pick the border interpolation algorithm.
* The different algorithms are illustrated here:
* @see {@link https://vovkos.github.io/doxyrest-showcase/opencv/sphinx_rtd_theme/enum_cv_BorderTypes.html}
* @param type - The border type.
* @param value - A pixel value if BorderType.CONSTANT is used.
* @returns The border interpolation function.
*/
export function getBorderInterpolation(
type: BorderType,
value: number | number[],
): BorderInterpolationFunction {
if (typeof value === 'number') {
value = new Array(4).fill(value);
}
return match(type)
.with('constant', () => getInterpolateConstant(value))
.with('replicate', () => interpolateReplicate)
.with('reflect', () => interpolateReflect)
.with('reflect101', () => interpolateReflect101)
.with('wrap', () => interpolateWrap)
.exhaustive();
}
function checkRange(point: number, length: number): void {
if (point <= 0 - length || point >= length + length - 1) {
throw new RangeError('border must be smaller than the original image');
}
}
function getInterpolateConstant(value: number[]): BorderInterpolationFunction {
return function interpolateConstant(
column: number,
row: number,
channel: number,
image: Image,
): number {
const newColumn = interpolateConstantPoint(column, image.width);
const newRow = interpolateConstantPoint(row, image.height);
if (newColumn === -1 || newRow === -1) {
return value[channel];
}
return image.getValue(newColumn, newRow, channel);
};
}
/**
* Interpolate using a constant point.
* @param point - The point to interpolate.
* @param length - The length of the image.
* @returns The interpolated point.
*/
export function interpolateConstantPoint(
point: number,
length: number,
): number {
if (point >= 0 && point < length) {
return point;
}
return -1;
}
function interpolateReplicate(
column: number,
row: number,
channel: number,
image: Image,
): number {
return image.getValue(
interpolateReplicatePoint(column, image.width),
interpolateReplicatePoint(row, image.height),
channel,
);
}
/**
* Interpolate by replicating the border.
* @param point - The point to interpolate.
* @param length - The length of the image.
* @returns The interpolated point.
*/
export function interpolateReplicatePoint(
point: number,
length: number,
): number {
if (point >= 0 && point < length) {
return point;
}
checkRange(point, length);
if (point < 0) {
return 0;
} else {
return length - 1;
}
}
function interpolateReflect(
column: number,
row: number,
channel: number,
image: Image,
): number {
return image.getValue(
interpolateReflectPoint(column, image.width),
interpolateReflectPoint(row, image.height),
channel,
);
}
/**
* Interpolate by reflecting the border.
* @param point - The point to interpolate.
* @param length - The length of the image.
* @returns The interpolated point.
*/
export function interpolateReflectPoint(point: number, length: number): number {
if (point >= 0 && point < length) {
return point;
}
checkRange(point, length);
if (point < 0) {
return -1 - point;
} else {
return length + length - 1 - point;
}
}
function interpolateWrap(
column: number,
row: number,
channel: number,
image: Image,
): number {
return image.getValue(
interpolateWrapPoint(column, image.width),
interpolateWrapPoint(row, image.height),
channel,
);
}
/**
* Interpolate by wrapping the border.
* @param point - The point to interpolate.
* @param length - The length of the image.
* @returns The interpolated point.
*/
export function interpolateWrapPoint(point: number, length: number): number {
if (point >= 0 && point < length) {
return point;
}
checkRange(point, length);
if (point < 0) {
return length + point;
} else {
return point - length;
}
}
function interpolateReflect101(
column: number,
row: number,
channel: number,
image: Image,
): number {
return image.getValue(
interpolateReflect101Point(column, image.width),
interpolateReflect101Point(row, image.height),
channel,
);
}
/**
* Interpolate by reflecting the border.
* @param point - The point to interpolate.
* @param length - The length of the image.
* @returns The interpolated point.
*/
export function interpolateReflect101Point(
point: number,
length: number,
): number {
if (point >= 0 && point < length) {
return point;
}
checkRange(point, length);
if (point < 0) {
return 0 - point;
} else {
return length + length - 2 - point;
}
}