UNPKG

homography-transform

Version:

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

83 lines (82 loc) 3.3 kB
export class PlaneTransformer { 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 = []; // Build the system of equations Ah = b for (let i = 0; i < numPoints; i++) { const { source, target } = this.correspondingPoints[i]; const { x, y } = source; const { x: X, y: Y } = target; // Add equations for x coordinate A.push([x, y, 1, 0, 0, 0, -x * X, -y * X]); b.push(X); // Add equations for y coordinate A.push([0, 0, 0, x, y, 1, -x * Y, -y * Y]); b.push(Y); } try { // Solve the system using least squares const h = numeric.solve(A, b); // Reshape the solution into a 3x3 matrix this.homographyMatrix = [ [h[0], h[1], h[2]], [h[3], h[4], h[5]], [h[6], h[7], 1.0] ]; } 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; // Apply homography transformation const w = H[2][0] * x + H[2][1] * y + 1.0; 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; } }