UNPKG

homography-transform

Version:

A robust TypeScript implementation of homography-based transformation between 2D planes, ideal for computer vision and image mapping

104 lines (102 loc) 3.24 kB
// src/Plane.ts var Plane = class { constructor(width, height) { this.width = width; this.height = height; if (width <= 0 || height <= 0) { throw new Error("Plane dimensions must be positive numbers"); } } getWidth() { return this.width; } getHeight() { return this.height; } isPointInBounds(point) { return point.x >= 0 && point.x <= this.width && point.y >= 0 && point.y <= this.height; } toString() { return `Plane(${this.width}x${this.height})`; } }; // src/PlaneTransformer.ts var PlaneTransformer = class { constructor(sourcePlane, targetPlane, correspondingPoints) { this.sourcePlane = sourcePlane; this.targetPlane = targetPlane; this.correspondingPoints = correspondingPoints; this.homographyMatrix = null; if (correspondingPoints.length < 4) { throw new Error("At least 4 point pairs are required for homography transformation"); } this.validatePoints(); this.computeHomography(); } validatePoints() { for (const pair of this.correspondingPoints) { if (!this.sourcePlane.isPointInBounds(pair.source)) { throw new Error(`Source point ${JSON.stringify(pair.source)} is out of bounds`); } if (!this.targetPlane.isPointInBounds(pair.target)) { throw new Error(`Target point ${JSON.stringify(pair.target)} is out of bounds`); } } } computeHomography() { const numPoints = this.correspondingPoints.length; const A = []; const b = []; for (let i = 0; i < numPoints; i++) { const { source, target } = this.correspondingPoints[i]; const { x, y } = source; const { x: X, y: Y } = target; A.push([x, y, 1, 0, 0, 0, -x * X, -y * X]); b.push(X); A.push([0, 0, 0, x, y, 1, -x * Y, -y * Y]); b.push(Y); } try { const h = numeric.solve(A, b); this.homographyMatrix = [ [h[0], h[1], h[2]], [h[3], h[4], h[5]], [h[6], h[7], 1] ]; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error("Failed to compute homography matrix: " + errorMessage); } } transform(point) { if (!this.homographyMatrix) { throw new Error("Homography matrix has not been computed"); } if (!this.sourcePlane.isPointInBounds(point)) { throw new Error("Source point is out of bounds"); } const H = this.homographyMatrix; const { x, y } = point; const w = H[2][0] * x + H[2][1] * y + 1; const transformedX = (H[0][0] * x + H[0][1] * y + H[0][2]) / w; const transformedY = (H[1][0] * x + H[1][1] * y + H[1][2]) / w; return { x: transformedX, y: transformedY }; } getTransformationError() { if (!this.homographyMatrix) { throw new Error("Homography matrix has not been computed"); } let totalError = 0; for (const pair of this.correspondingPoints) { const transformed = this.transform(pair.source); const dx = transformed.x - pair.target.x; const dy = transformed.y - pair.target.y; totalError += Math.sqrt(dx * dx + dy * dy); } return totalError / this.correspondingPoints.length; } }; export { Plane, PlaneTransformer };