UNPKG

image-js

Version:

Image processing and manipulation in JavaScript

108 lines 4.8 kB
import { Matrix, inverse } from 'ml-matrix'; import { Image } from '../Image.js'; import { getClamp } from '../utils/clamp.js'; import { getDefaultColor } from "../utils/getDefaultColor.js"; import { getBorderInterpolation } from '../utils/interpolateBorder.js'; import { getInterpolationFunction } from '../utils/interpolatePixel.js'; import { validateColor } from "../utils/validators/validators.js"; /** * Transforms an image using a matrix. * @param image - Original image. * @param transformMatrix - 2×3 transform matrix. * @param options - Transform options. * @returns The new image. */ export function transform(image, transformMatrix, options = {}) { const { borderValue = getDefaultColor(image), borderType = 'constant', interpolationType = 'bilinear', fullImage, } = options; let { width = image.width, height = image.height } = options; if (Array.isArray(borderValue)) { validateColor(borderValue, image); } if (!isValidMatrix(transformMatrix)) { throw new TypeError(`transformation matrix must be 2x3 or 3x3. Received ${transformMatrix.length}x${transformMatrix[1].length}`); } if (transformMatrix.length === 2) { transformMatrix.push([0, 0, 1]); } if (fullImage) { transformMatrix = transformMatrix.map((row) => row.slice()); transformMatrix[0][2] = 0; transformMatrix[1][2] = 0; const corners = [ image.getCoordinates('top-left'), image.getCoordinates('top-right'), image.getCoordinates('bottom-right'), image.getCoordinates('bottom-left'), ]; corners[1].column += 1; corners[2].column += 1; corners[2].row += 1; corners[3].row += 1; const transformedCorners = corners.map((corner) => { return [ transformPoint(transformMatrix[0], transformMatrix[2], corner.column, corner.row), transformPoint(transformMatrix[1], transformMatrix[2], corner.column, corner.row), ]; }); const xCoordinates = transformedCorners.map((c) => c[0]); const yCoordinates = transformedCorners.map((c) => c[1]); const maxX = Math.max(...xCoordinates); const maxY = Math.max(...yCoordinates); const minX = Math.min(...xCoordinates); const minY = Math.min(...yCoordinates); const center = [(image.width - 1) / 2, (image.height - 1) / 2]; width = maxX - minX; height = maxY - minY; const centerX = transformPoint(transformMatrix[0], transformMatrix[2], center[0], center[1]); const centerY = transformPoint(transformMatrix[1], transformMatrix[2], center[0], center[1]); const a = (width - 1) / 2 - centerX; const b = (height - 1) / 2 - centerY; transformMatrix[0][2] = a; transformMatrix[1][2] = b; width = Math.round(width); height = Math.round(height); } if (!options.inverse) { transformMatrix = inverse(new Matrix(transformMatrix)).to2DArray(); } const newImage = Image.createFrom(image, { width, height, }); const interpolateBorder = getBorderInterpolation(borderType, borderValue); const clamp = getClamp(newImage); const interpolate = getInterpolationFunction(interpolationType); for (let row = 0; row < newImage.height; row++) { for (let column = 0; column < newImage.width; column++) { const nx = transformPoint(transformMatrix[0], transformMatrix[2], column, row); const ny = transformPoint(transformMatrix[1], transformMatrix[2], column, row); for (let channel = 0; channel < newImage.channels; channel++) { const newValue = interpolate(image, nx, ny, channel, interpolateBorder, clamp); newImage.setValue(column, row, channel, newValue); } } } return newImage; } /** * Apply a transformation to a point. * @param transform - Transformation matrix. * @param perspective - Perspective matrix. * @param column - Column of the point. * @param row - Row of the point. * @returns New value. */ function transformPoint(transform, perspective, column, row) { return ((transform[0] * column + transform[1] * row + transform[2]) / (perspective[0] * column + perspective[1] * row + perspective[2])); } function isValidMatrix(transformationMatrix) { return ((transformationMatrix.length === 3 && transformationMatrix[0].length === 3 && transformationMatrix[1].length === 3 && transformationMatrix[2].length === 3) || (transformationMatrix.length === 2 && transformationMatrix[0].length === 3 && transformationMatrix[1].length === 3)); } //# sourceMappingURL=transform.js.map