You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:将Python OpenCV中的Procrustes变换代码转换为Java

Converting Python Procrustes Alignment to Java

Hey there, I’ve spent some time working through this exact problem—converting that Procrustes-based affine transformation function to Java. The core logic (centroid subtraction, scaling, SVD for rotation) translates directly, but we’ll use Apache Commons Math for matrix operations since Java’s standard library doesn’t have built-in SVD or robust matrix utilities.

Step-by-Step Implementation

First, add the Apache Commons Math dependency to your project (if using Maven):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.6.1</version>
</dependency>

Here’s the full Java implementation, mirroring the Python logic you described:

import org.apache.commons.math3.linear.*;

public class ProcrustesAlignment {

    public static RealMatrix transformationFromPoints(RealMatrix points1, RealMatrix points2) {
        // Validate input dimensions (expect 68x2 matrices)
        if (points1.getRowDimension() != 68 || points1.getColumnDimension() != 2 ||
            points2.getRowDimension() != 68 || points2.getColumnDimension() != 2) {
            throw new IllegalArgumentException("Input points must be 68x2 matrices");
        }

        // Step 1: Calculate centroids of both point sets
        RealVector centroid1 = calculateCentroid(points1);
        RealVector centroid2 = calculateCentroid(points2);

        // Step 2: Shift points to be relative to their centroids (center at origin)
        RealMatrix centered1 = centerPoints(points1, centroid1);
        RealMatrix centered2 = centerPoints(points2, centroid2);

        // Step 3: Normalize point sets by their root-mean-square scale
        double scale1 = calculateScale(centered1);
        double scale2 = calculateScale(centered2);
        RealMatrix normalized1 = centered1.scalarMultiply(1 / scale1);
        RealMatrix normalized2 = centered2.scalarMultiply(1 / scale2);

        // Step 4: Use SVD to find optimal rotation matrix
        SingularValueDecomposition svd = new SingularValueDecomposition(normalized2.transpose().multiply(normalized1));
        RealMatrix u = svd.getU();
        RealMatrix vt = svd.getVT();
        RealMatrix rotation = u.multiply(vt);

        // Ensure rotation has positive determinant (avoid reflection)
        if (rotation.getDeterminant() < 0) {
            RealMatrix v = svd.getV();
            v.setColumn(1, v.getColumnVector(1).mapMultiply(-1));
            rotation = u.multiply(v.transpose());
        }

        // Step 5: Compute overall scaling factor
        double scale = scale2 / scale1;

        // Step 6: Calculate translation vector T = centroid2 - s*R*centroid1
        RealVector translation = centroid2.subtract(rotation.multiply(centroid1).mapMultiply(scale));

        // Assemble 3x3 affine transformation matrix [s*R | T; 0 0 1]
        RealMatrix transform = MatrixUtils.createRealMatrix(3, 3);
        // Fill scaled rotation portion
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                transform.setEntry(i, j, rotation.getEntry(i, j) * scale);
            }
        }
        // Fill translation portion
        transform.setEntry(0, 2, translation.getEntry(0));
        transform.setEntry(1, 2, translation.getEntry(1));
        // Homogeneous coordinate row
        transform.setEntry(2, 2, 1.0);

        return transform;
    }

    private static RealVector calculateCentroid(RealMatrix points) {
        int numPoints = points.getRowDimension();
        double xMean = points.getColumnVector(0).getSum() / numPoints;
        double yMean = points.getColumnVector(1).getSum() / numPoints;
        return MatrixUtils.createRealVector(new double[]{xMean, yMean});
    }

    private static RealMatrix centerPoints(RealMatrix points, RealVector centroid) {
        RealMatrix centered = points.copy();
        for (int i = 0; i < points.getRowDimension(); i++) {
            centered.setRowVector(i, centered.getRowVector(i).subtract(centroid));
        }
        return centered;
    }

    private static double calculateScale(RealMatrix centeredPoints) {
        // Compute root mean square of all point coordinates
        double sumSquares = 0;
        int numPoints = centeredPoints.getRowDimension();
        for (int i = 0; i < numPoints; i++) {
            sumSquares += centeredPoints.getRowVector(i).getNormSq();
        }
        return Math.sqrt(sumSquares / numPoints);
    }

    // Example usage
    public static void main(String[] args) {
        // Populate with your actual 68x2 point data
        RealMatrix points1 = MatrixUtils.createRealMatrix(68, 2);
        RealMatrix points2 = MatrixUtils.createRealMatrix(68, 2);

        RealMatrix transform = transformationFromPoints(points1, points2);
        System.out.println("Affine Transformation Matrix:");
        System.out.println(transform);
    }
}

Key Details

  • Centroid Shifting: We move both point sets to be centered at the origin to eliminate translation differences before computing rotation.
  • Normalization: Scaling each point set to have a root-mean-square size of 1 ensures we isolate rotation from scale differences.
  • Rotation via SVD: The optimal rotation comes from multiplying the U and V^T matrices from the SVD of normalized2^T * normalized1. We flip a column in V if the determinant is negative to avoid reflection matrices.
  • Affine Matrix: The final 3x3 matrix combines scaled rotation, translation, and a homogeneous row to support standard 2D affine transformation operations.

This implementation will produce the same result as your Python function, minimizing the sum of squared distances sum ||s*R*p1,i + T - p2,i||².

内容的提问来源于stack exchange,提问作者Oga Emma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:36:09