Skip to content

Commit

Permalink
The JAMA subproject now provides its own solver implementation for im…
Browse files Browse the repository at this point in the history
…age-registration.
  • Loading branch information
Oliver-Loeffler committed Apr 24, 2023
1 parent f55c724 commit 480f3b2
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 1 deletion.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ The solver is selected using the Java SPI (Service Provider Interface) mechanism
- [x] Versions up to and including 0.0.5 run with Java-8
- [x] Version 0.0.5 will support different linear algebra libraries (will make use of service provider API)
- [x] Version 0.0.6 will support ~~Java-8 and~~ Java-11 (~~utilize multi-release JARs~~ support for modules will be introduced)
- [ ] Version 0.0.7 will support Java-17 with records (JEP 359)
- [ ] Version 0.0.7 will no longer provide a bundle version, the core is now the `image-registration` API library. It is now mandatory to add the required solver as needed.
- [ ] Version 0.0.8 will support Java-17 with records (JEP 359)
- [ ] Later versions will support higher order calculations (first: up to 3rd order, 20 coefficient model)

These methods are used e.g. in photomask manufacturing, medical imaging or geospatial applications.
Expand Down
4 changes: 4 additions & 0 deletions jama/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ plugins {
id 'de.jjohannes.extra-java-module-info'
}

dependencies {
implementation project(':solver-api')
}

test {
useJUnitPlatform()
}
Expand Down
5 changes: 5 additions & 0 deletions jama/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/**
* JAMA: A Java Matrix Package
* (also usable as a solver for image registration)
*/
open module net.raumzeitfalle.jama {
requires transitive net.raumzeitfalle.registration.solver;
exports net.raumzeitfalle.jama;

provides net.raumzeitfalle.registration.solver.spi.SolverAdapter
with net.raumzeitfalle.jama.Solver;
}
59 changes: 59 additions & 0 deletions jama/src/main/java/net/raumzeitfalle/jama/Solver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*-
* #%L
* Image-Registration
* %%
* Copyright (C) 2019, 2021 Oliver Loeffler, Raumzeitfalle.net
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package net.raumzeitfalle.jama;

import net.raumzeitfalle.registration.solver.Deltas;
import net.raumzeitfalle.registration.solver.References;
import net.raumzeitfalle.registration.solver.Solution;
import net.raumzeitfalle.registration.solver.Solutions;
import net.raumzeitfalle.registration.solver.spi.SolverAdapter;

public class Solver implements SolverAdapter {

public Solution solve(References references, Deltas deviations) {

Matrix refs = new Matrix(references.getArray());
Matrix deltas = new Matrix(deviations.getArray(), deviations.rows());

QRDecomposition qr = new QRDecomposition(refs);

Matrix rInverse = qr.getR().inverse();
Matrix qTransposed = qr.getQ().transpose();
Matrix solution = rInverse.times(qTransposed)
.times(deltas);

double[] firstColumn = getFirstColumn(solution);
return Solutions.fromArray(firstColumn);

}

private double[] getFirstColumn(Matrix solution) {
double[] column = new double[solution.getRowDimension()];
for (int row = 0; row < column.length; row++) {
column[row] = solution.get(row, 0);
}
return column;
}

@Override
public Solution apply(References t, Deltas u) {
return solve(t, u);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
net.raumzeitfalle.jama.Solver
67 changes: 67 additions & 0 deletions jama/src/test/java/net/raumzeitfalle/jama/SolverTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*-
* #%L
* Image-Registration
* %%
* Copyright (C) 2019, 2021 Oliver Loeffler, Raumzeitfalle.net
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package net.raumzeitfalle.jama;

import net.raumzeitfalle.registration.solver.Solution;
import net.raumzeitfalle.registration.solver.spi.SolverAdapter;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class SolverTest {

private final SolverAdapter classUnderTest = new Solver();

private static final double TOLERANCE = 1E-11;

@Test
void test() {

double[][] design = {
{ -75000.0, 0.0, 0.0, -70000.0, 1.0, 0.0 },
{ 0.0, -70000.0, 75000.0, 0.0, 0.0, 1.0 },
{ -75000.0, 0.0, 0.0, 70000.0, 1.0, 0.0 },
{ 0.0, 70000.0, 75000.0, 0.0, 0.0, 1.0 },
{ 75000.0, 0.0, 0.0, 70000.0, 1.0, 0.0 },
{ 0.0, 70000.0, -75000.0, 0.0, 0.0, 1.0 },
{ 75000.0, 0.0, 0.0, -70000.0, 1.0, 0.0 },
{ 0.0, -70000.0, -75000.0, 0.0, 0.0, 1.0 } };

double[] differences = { -0.075, 0.140, -0.075, -0.140, 0.075, -0.140, 0.075, 0.140 };


Solution solution = classUnderTest.apply(() -> design, () -> differences);

double[] result = { 1.0E-6, -2.0E-6, 0.0, 0.0, 0.0, 0.0 };

assertAll(() -> assertNotNull(solution, "must not be null"),

() -> assertEquals(result[0], solution.get(0), TOLERANCE, "scale x"),
() -> assertEquals(result[1], solution.get(1), TOLERANCE, "scale y"),

() -> assertEquals(result[2], solution.get(2), TOLERANCE, "ortho x"),
() -> assertEquals(result[3], solution.get(3), TOLERANCE, "ortho y"),

() -> assertEquals(result[4], solution.get(4), TOLERANCE, "trans x"),
() -> assertEquals(result[5], solution.get(5), TOLERANCE, "trans y"));

}

}
1 change: 1 addition & 0 deletions solver-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
dependencies {
implementation project(':solver-api')
implementation project(':image-registration')
testImplementation project(':jama')
testImplementation project(':jama-solver')
testImplementation project(':la4j-solver')
testImplementation project(':ejml-solver')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*-
* #%L
* Image-Registration
* %%
* Copyright (C) 2019, 2021 Oliver Loeffler, Raumzeitfalle.net
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package net.raumzeitfalle.registration.solvertest;

import net.raumzeitfalle.jama.Solver;
import net.raumzeitfalle.registration.solver.SolverProvider;
import net.raumzeitfalle.registration.solver.spi.SolverAdapter;
import net.raumzeitfalle.registration.solvertest.numerics.AffineTransformNumerics;
import net.raumzeitfalle.registration.solvertest.numerics.RigidTransformNumerics;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;


class IntegratedJamaNumericsTest {

private final RigidTransformNumerics rigidTransformNumerics = new RigidTransformNumerics();

private final AffineTransformNumerics affineTransformNumerics = new AffineTransformNumerics();

@BeforeAll
public static void prepare() {
SolverProvider.setPreferredImplementation(Solver.class.getName());
}

@BeforeEach
void checkImplementation() {
SolverAdapter solver = new Solver();
assertEquals(solver.getClass(), SolverProvider.getInstance().getSolver().getClass());
}

@Test
void translationX() {
rigidTransformNumerics.assertTranslationX();
}

@Test
void translationY() {
rigidTransformNumerics.assertTranslationY();
}

@Test
void translationXY() {
rigidTransformNumerics.assertTranslationXY();
}

@Test
void rotation() {
rigidTransformNumerics.assertRotation();
}

@Test
void rotationAndTranslation() {
rigidTransformNumerics.assertRotationAndTranslation();
}

@Test
void skipTransform() {
rigidTransformNumerics.assertSkipTransform();
}

@Test
void translationXonly1D() {
rigidTransformNumerics.assertTranslationXonly1D();
}


@Test
void translationYonly1D() {
rigidTransformNumerics.assertTranslationYonly1D();
}

@Test
void alignmentOfDisplacementsAlongHorizontalLine() {
rigidTransformNumerics.assertDisplacementsAlongHorizontalLine();
}

@Test
void singularityXY() {
rigidTransformNumerics.assertSingularityXY();
}

@Test
void singularityX() {
rigidTransformNumerics.assertSingularityX();
}

@Test
void singularityY() {
rigidTransformNumerics.assertSingularityY();
}

//
@Test
void zeroTransform() {
affineTransformNumerics.assertZeroTransform();
}

@Test
void scalingX() {
affineTransformNumerics.assertScalingX();
}

@Test
void scalingY_withoutX() {
affineTransformNumerics.assert_scalingY_withoutX();
}

@Test
void scalingX_withoutY() {
affineTransformNumerics.assert_scalingX_withoutY();
}

@Test
void scalingXY() {
affineTransformNumerics.assertScalingXY();
}

@Test
void shearingX() {
affineTransformNumerics.assertShearingX();
}

@Test
void shearingY() {
affineTransformNumerics.assertShearingY();
}

@Test
void shearingXY() {
affineTransformNumerics.assertShearingXY();
}

@Test
void displacementsAlongVerticalLine() {
affineTransformNumerics.assertDisplacementsAlongVerticalLine();
}

@Test
void displacementsAlongHorizontalLine() {
affineTransformNumerics.assertDisplacementsAlongHorizontalLine();
}
}

0 comments on commit 480f3b2

Please sign in to comment.