Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: Oliver-Loeffler/image-registration
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.0.7
Choose a base ref
...
head repository: Oliver-Loeffler/image-registration
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Apr 25, 2023

  1. Copy the full SHA
    35c2202 View commit details
  2. Copy the full SHA
    943a7da View commit details
  3. Copy the full SHA
    007ebf7 View commit details

Commits on Apr 26, 2023

  1. Fixed doc links.

    Oliver-Loeffler committed Apr 26, 2023
    Copy the full SHA
    120d26d View commit details
  2. Fixed doc links.

    Oliver-Loeffler committed Apr 26, 2023
    Copy the full SHA
    a099b15 View commit details

Commits on Jan 28, 2025

  1. Copy the full SHA
    e7c8793 View commit details
  2. Copy the full SHA
    c9db008 View commit details
  3. Copy the full SHA
    8e6222f View commit details
  4. Copy the full SHA
    acd5273 View commit details
  5. Copy the full SHA
    5a5114b View commit details
  6. Copy the full SHA
    f819fdf View commit details
  7. Copy the full SHA
    fe8899f View commit details
  8. Copy the full SHA
    16fbbe7 View commit details
  9. Copy the full SHA
    fce641c View commit details
  10. Update README.md

    Oliver-Loeffler authored Jan 28, 2025
    Copy the full SHA
    3a9d360 View commit details
  11. Copy the full SHA
    2dfba50 View commit details
  12. Copy the full SHA
    ec63956 View commit details

Commits on Jan 29, 2025

  1. Copy the full SHA
    dab4765 View commit details

Commits on Feb 13, 2025

  1. Copy the full SHA
    294eae1 View commit details
  2. Created some helper functions to introduce first higher order terms i…

    …n R, so there will be a basis the Java functions will be built upon.
    Oliver-Loeffler committed Feb 13, 2025
    Copy the full SHA
    1164b1b View commit details
  3. Copy the full SHA
    bd18c1c View commit details

Commits on Feb 16, 2025

  1. Copy the full SHA
    0678dab View commit details
  2. Copy the full SHA
    ca2907a View commit details

Commits on Feb 17, 2025

  1. Updated HO helper.

    Oliver-Loeffler committed Feb 17, 2025
    Copy the full SHA
    ed48005 View commit details
  2. Added helper comment.

    Oliver-Loeffler committed Feb 17, 2025
    Copy the full SHA
    cc70503 View commit details
  3. Copy the full SHA
    15477b3 View commit details
  4. Copy the full SHA
    3a9f022 View commit details
Showing with 1,275 additions and 259 deletions.
  1. +198 −10 README.md
  2. +3 −0 apache-math3-solver/src/main/java/module-info.java
  3. +20 −9 build.gradle
  4. +10 −17 ejml-simple-bundle/build.gradle
  5. +2 −3 ejml-solver/build.gradle
  6. +3 −0 ejml-solver/src/main/java/module-info.java
  7. +3 −0 example-modular/src/main/java/module-info.java
  8. +37 −13 ...ple-modular/src/main/java/net/raumzeitfalle/registration/examples/modular/DisplacementParser.java
  9. +80 −79 example-modular/src/main/java/net/raumzeitfalle/registration/examples/modular/FileLoader.java
  10. BIN example-non-modular/distortions_higher_order_noise_xy.png
  11. BIN example-non-modular/distortions_higher_order_xy.png
  12. BIN example-non-modular/distortions_noise_only.png
  13. BIN example-non-modular/distortions_noisy_scaling_shifting_skew_xy.png
  14. BIN example-non-modular/distortions_noisy_scaling_shifting_xy.png
  15. BIN example-non-modular/distortions_none.png
  16. BIN example-non-modular/distortions_scaling_extend_x.png
  17. BIN example-non-modular/distortions_scaling_extend_xy.png
  18. BIN example-non-modular/distortions_scaling_extend_y.png
  19. BIN example-non-modular/distortions_scaling_shifting_xy.png
  20. BIN example-non-modular/distortions_scaling_shrink_x.png
  21. BIN example-non-modular/distortions_scaling_shrink_xy.png
  22. BIN example-non-modular/distortions_scaling_shrink_y.png
  23. BIN example-non-modular/distortions_skew_x.png
  24. BIN example-non-modular/distortions_skew_x_inv.png
  25. BIN example-non-modular/distortions_skew_x_yinv.png
  26. BIN example-non-modular/distortions_skew_xinv_y.png
  27. BIN example-non-modular/distortions_skew_xinv_yinv.png
  28. BIN example-non-modular/distortions_skew_xy.png
  29. BIN example-non-modular/distortions_skew_y.png
  30. BIN example-non-modular/distortions_skew_y_inv.png
  31. BIN example-non-modular/distortions_translation_x.png
  32. BIN example-non-modular/distortions_translation_xy.png
  33. BIN example-non-modular/distortions_translation_y.png
  34. BIN example-non-modular/grid_plot_1.png
  35. BIN example-non-modular/grid_plot_2.png
  36. BIN example-non-modular/grid_plot_3.png
  37. +364 −0 example-non-modular/src/main/R/Helper.R
  38. +37 −13 ...n-modular/src/main/java/net/raumzeitfalle/registration/examples/classpath/DisplacementParser.java
  39. +80 −79 example-non-modular/src/main/java/net/raumzeitfalle/registration/examples/classpath/FileLoader.java
  40. BIN gradle/wrapper/gradle-wrapper.jar
  41. +2 −1 gradle/wrapper/gradle-wrapper.properties
  42. +20 −13 gradlew
  43. +12 −10 gradlew.bat
  44. +7 −1 image-registration/build.gradle
  45. +1 −0 image-registration/src/main/java/module-info.java
  46. +12 −0 image-registration/src/main/java/net/raumzeitfalle/registration/displacement/Displacement.java
  47. +71 −0 image-registration/src/main/java/net/raumzeitfalle/registration/displacement/DisplacementParser.java
  48. +29 −0 ...istration/src/main/java/net/raumzeitfalle/registration/displacement/DisplacementParsingError.java
  49. +78 −0 image-registration/src/main/java/net/raumzeitfalle/registration/displacement/FileLoader.java
  50. +152 −0 image-registration/src/main/java/net/raumzeitfalle/registration/distortions/HigherOrderEquation.java
  51. +35 −0 ...egistration/src/test/java/net/raumzeitfalle/registration/distortions/HigherOrderEquationTest.java
  52. +4 −0 jama-solver/src/main/java/module-info.java
  53. +3 −8 jama-solver/src/test/java/net/raumzeitfalle/registration/jama/JamaSolverTest.java
  54. +1 −1 jama/src/main/java/net/raumzeitfalle/jama/QRDecomposition.java
  55. +2 −2 jama/src/main/java/net/raumzeitfalle/jama/SingularValueDecomposition.java
  56. +3 −0 jblas-solver/src/main/java/module-info.java
  57. +3 −0 la4j-solver/src/main/java/module-info.java
  58. +3 −0 solver-api/src/main/java/module-info.java
208 changes: 198 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
# Image Registration using Control Points

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Javadocs](https://www.javadoc.io/badge/net.raumzeitfalle.registration/image-registration.svg)](https://www.javadoc.io/doc/net.raumzeitfalle.registration/image-registration) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=net.raumzeitfalle.registration%3Aimage-registration&metric=alert_status)](https://sonarcloud.io/dashboard?id=net.raumzeitfalle.registration%3Aimage-registration)
[![codecov](https://codecov.io/gh/Oliver-Loeffler/image-registration/branch/master/graph/badge.svg)](https://codecov.io/gh/Oliver-Loeffler/image-registration) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.raumzeitfalle.registration/image-registration/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.raumzeitfalle.registration/image-registration)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

Image Registration API:

[![Javadocs](https://www.javadoc.io/badge/net.raumzeitfalle.registration/image-registration.svg)](https://www.javadoc.io/doc/net.raumzeitfalle.registration/image-registration) [![Maven Central](https://img.shields.io/maven-central/v/net.raumzeitfalle.registration/image-registration)](https://search.maven.org/search?q=net.raumzeitfalle.registration) [![GitHub issues](https://img.shields.io/github/issues/oliver-loeffler/image-registration)](https://github.com/Oliver-Loeffler/image-registration/issues) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=net.raumzeitfalle.registration%3Aimage-registration&metric=coverage)](https://sonarcloud.io/summary/new_code?id=net.raumzeitfalle.registration%3Aimage-registration)

Solver-API:

[![Javadocs](https://www.javadoc.io/badge/net.raumzeitfalle.registration/image-registration.svg)](https://www.javadoc.io/doc/net.raumzeitfalle.registration/solver-api)

## TL;DR;

Image registration is the process of finding the transform to match a given image with a desired reference. This library supports rigid body transforms (translation and rotation) as well as affine transforms (anisotropic scaling, anisotropic rotation/shear aka. non-orthogonality). Currently non-linear transforms are not supported.

Up to version 0.0.4, this API only worked together with NIST JAMA. Since version 0.0.5, different linear algebra frameworks can be used in `image-registration` using the Solver-API. Here it is now possible to choose either `gov.nist.math.jama`,
`org.la4j`, `org.ejml`, `org.apache.commons.math3` or even `org.jblas`. The library now consists of an API and a core JAR and a group of JARs providing a solver implementation for the linear algebra framework of choice.
The solver is selected using the Java SPI (Service Provider Interface) mechanism.
The solver is selected using the Java SPI (Service Provider Interface) mechanism. With version 0.0.7, selection of a solver implementation is mandatory as the main artifact no longer includes a default solver implementation.

- [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 no longer provide a bundle version, the core is now the `image-registration` API library. It will be mandatory to add the required solver as needed.
- [x] Version 0.0.7 will no longer provide a bundle version, the core is now the `image-registration` API library. It will be 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)

@@ -24,24 +31,27 @@ Control point or feature based methods have only limited scope of use in medical
* Photomask related:http://www.lithoguru.com/scientist/glossary/R.html#registration, http://www.lithoguru.com/scientist/glossary/O.html#overlay, https://www.nist.gov/system/files/documents/pml/div683/conference/Hughes.pdf
* Medical imaging: http://www.iro.umontreal.ca/~sherknie/articles/medImageRegAnOverview/brussel_bvz.pdf
* Geo-Registration: http://www.georeference.org/doc/georegistration.htm
* Chris A. Macks Youtube Channel: https://www.youtube.com/@ChrisMack

## How to start?

The SNAPSHOT-API documentation is available on: https://www.raumzeitfalle.net/image-registration/api/
Version 0.0.5 is available on Maven Central using following snippet:
The SNAPSHOT-API documentation is available on: https://www.raumzeitfalle.net/image-registration/reg/
The javadoc for the Solver-API: https://www.raumzeitfalle.net/image-registration/solver/

Version 0.0.7 is available on Maven Central using following snippet:

### Maven Dependency for a project with JAMA backend (using `gov.nist.math:jama:1.0.3`):

```xml
<dependency>
<groupId>net.raumzeitfalle.registration</groupId>
<artifactId>image-registration</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>
</dependency>
<dependency>
<groupId>net.raumzeitfalle.registration</groupId>
<artifactId>jama-solver</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>
</dependency>
```

@@ -61,12 +71,12 @@ module yourmodule {
<dependency>
<groupId>net.raumzeitfalle.registration</groupId>
<artifactId>solver-api</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>
</dependency>
<dependency>
<groupId>net.raumzeitfalle.registration</groupId>
<artifactId>la4j-solver</artifactId>
<version>0.0.6</version>
<version>0.0.7</version>
</dependency>
```

@@ -135,6 +145,7 @@ In case a custom implementaton is required, this must be created based on `solve
* Sum up learnings and reshape structure of alignment/correction classes, possibly add
higher level functions to do the all-in-one-job as its done in the demos.
* Add graphical examples of first order distortions.
* Functionality to determine which kind of correction / model can be used on a given dataset

# How does is work?

@@ -307,6 +318,178 @@ be used.
System.out.println(correctedFirstOrder);
```

## Transform types

| Symbol | Description |
| ------ | ---------------------------------------------------------------- |
| + | is directly calculated by model |
| o | can be approximated from results |
| sx/sy | anisotropic scaling (scale x/y) |
| mag | magnification, isotropic scaling (mag = average(sx,sy)) |
| ox/oy | anisotropic rotation (shearing or non-orthogonality, ortho x/y) |
| ortho | isotropic non-orthogonality (ortho = oy - ox) |
| rot | isotropic rotation |
| tx/ty | translation in x/y direction |


| Transform | tx | ty | sx | sy | mag | ox | oy | ortho | rot |
| ---------------------- | --- | --- | --- | --- | --- | --- | --- | ----- | --- |
| Rigid | + | + | | | | | | | + |
| Affine | + | + | + | + | o | + | + | + | o |
| Similarity | + | + | | | + | | | o | + |
| Non-Uniform Similarity | + | + | + | + | o | | | o | + |


## Cases

### #1 common case, references and readings in 2D (X,Y)

![2D references with readings in XY](docs/images/01_reference-2D_readings-XY.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | > 1 | > 1 |
| Y (count) | > 1 | > 1 |

* Input: X,Y,Xd,Yd
* Result: tx,ty,sx,sy,ox,oy

### #2 special case, references 2D, readings 1D (X)

![2D references with readings in X](docs/images/02_reference-2D_readings-X.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | > 1 | > 1 |
| Y (count) | > 1 | = 0 |

* Input: X,Y,Xd
* Result: tx,sx,ox

### #3 special case, references 2D, readings 1D (Y)

![2D references with readings in Y](docs/images/03_reference-2D_readings-Y.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | > 1 | = 0 |
| Y (count) | > 1 | > 1 |

* Input: X,Y,Yd
* Result: ty,sy,oy

### #4 common case, references in 2D, most readings (X,Y), *some readings (X or Y)*

![2D references with partially missing X or Y](docs/images/04_reference-2D_readings-XY-partial-X-Y.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | > 1 | > 1 |
| Y (count) | > 1 | > 1 |

* Input: X,Y,Xd,Yd; __*some readings have either Xd or Yd only*__
* Result: tx,ty,sx,sy,ox,oy

### #5 special case, references along horizontal line, readings in 2D (X,Y)

![references along horizontal line (1D) with X and Y](docs/images/05_reference-1D-hor_readings-XY.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | > 1 | > 1 |
| Y (count) | = 1 | > 1 |

* Input: X,Y,Xd,Yd
* Result: tx,ty,sx,ox

### #6 special case, references along vertical line, readings in 2D (X,Y)

![references along vertical line (1D) with X and Y](docs/images/06_reference-1D-ver_readings-XY.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | = 1 | > 1 |
| Y (count) | > 1 | > 1 |

* Input: X,Y,Xd,Yd
* Result: tx,ty,sy,oy

### #7 special case, references along horizontal line, readings in 1D (X)

![references along horizontal line (1D) with X only](docs/images/07_reference-1D-hor_readings-X.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | > 1 | > 1 |
| Y (count) | = 1 | = 0 |

* Input: X,Y,Xd
* Result: tx,sx

### #8 special case, references along horizontal line, readings in 1D (Y)

![references along horizontal line (1D) with Y only](docs/images/08_reference-1D-hor_readings-Y.png)

| Direction | Refs | Readings |
| --------- | -----| -------- |
| X (count) | > 1 | = 0 |
| Y (count) | = 1 | > 1 |

* Input: X,Y,Yd
* Result: ty,ox

### #9 special case, references along vertical line, readings in 1D (X)
### #10 special case, references along vertical line, readings in 1D (Y)
### #11 special case, only one reference position, readings in (X and Y) or (X or Y)

![references on one single location, partially with X and Y, some XY](docs/images/11_single-reference-singularity.png)

## Distortion Types Overview (incomplete)

### Noise and No-Noise
![Perfect](example-non-modular/distortions_none.png)
![Noise only](example-non-modular/distortions_noise_only.png)

### Translation
![translation x](example-non-modular/distortions_translation_x.png)
![translation y](example-non-modular/distortions_translation_y.png)
![translation xy](example-non-modular/distortions_translation_xy.png)

### Scaling
![shrink x](example-non-modular/distortions_scaling_shrink_x.png)
![extend x](example-non-modular/distortions_scaling_extend_x.png)

![shrink y](example-non-modular/distortions_scaling_shrink_y.png)
![extend y](example-non-modular/distortions_scaling_extend_y.png)

![shrink xy](example-non-modular/distortions_scaling_shrink_xy.png)
![extend xy](example-non-modular/distortions_scaling_extend_xy.png)

### Skew
![skew x](example-non-modular/distortions_skew_x.png)
![skew y](example-non-modular/distortions_skew_y.png)
![skew xy](example-non-modular/distortions_skew_xy.png)

![skew -x](example-non-modular/distortions_skew_x_inv.png)
![skew -y](example-non-modular/distortions_skew_y_inv.png)
![skew -x,-y](example-non-modular/distortions_skew_xinv_yinv.png)

![skew x -y](example-non-modular/distortions_skew_x_yinv.png)
![skew -x y](example-non-modular/distortions_skew_xinv_y.png)

### Combination

![scaling and shifting](example-non-modular/distortions_scaling_shifting_xy.png)
![scaling, shifting, noise](example-non-modular/distortions_noisy_scaling_shifting_xy.png)
![scaling, shifting, skew, noise](example-non-modular/distortions_noisy_scaling_shifting_skew_xy.png)

### Higher Order

![scaling, skew, high order](example-non-modular/distortions_higher_order_xy.png)
![scaling, skew, noise, high order](example-non-modular/distortions_higher_order_noise_xy.png)



# References for further reading

## Links
@@ -341,3 +524,8 @@ https://math.nist.gov/javanumerics/jama/#license.

Also, this project uses LA4J library, which also follows Apache 2.0 license.
See http://la4j.org for details, sources can be found at https://github.com/vkostyukov/la4j.

Badges have been created with:

* https://www.javadoc.io
* https://shields.io
3 changes: 3 additions & 0 deletions apache-math3-solver/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/**
* Apache Math Commons 3 Solver Module for Image-Registration
*/
open module net.raumzeitfalle.registration.mathcommons {
requires transitive net.raumzeitfalle.registration.solver;
requires org.apache.commonsmath3;
29 changes: 20 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ plugins {
id "java"
id "jacoco"
id "com.github.hierynomus.license-base" version "0.16.1"
id "org.sonarqube" version "3.0"
id "de.jjohannes.extra-java-module-info" version "0.11"
id "org.sonarqube" version "6.0.1.5171"
id "de.jjohannes.extra-java-module-info" version "0.16"
}

allprojects {
@@ -19,21 +19,31 @@ allprojects {
apply plugin: 'com.github.hierynomus.license-base'
apply plugin: 'signing'
apply plugin: 'project-report'

group = 'net.raumzeitfalle.registration'
version = '0.0.7'
version = '0.0.8-SNAPSHOT'

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
withJavadocJar()
withJavadocJar()
withSourcesJar()
}

jacocoTestReport {
reports {
xml.required = true
}
}

test {
finalizedBy jacocoTestReport
}

dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.4'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.4'
}

tasks.withType(GenerateModuleMetadata) {
@@ -46,6 +56,7 @@ allprojects {
property "sonar.projectKey", System.getenv('SONARCLOUD_PROJECT_KEY')
property "sonar.login", System.getenv('SONARCLOUD_LOGIN')
property "sonar.organization", System.getenv('SONARCLOUD_ORG')
property 'sonar.coverage.jacoco.xmlReportPaths', "./solver-test/build/reports/jacoco/testCodeCoverageReport/testCodeCoverageReport.xml"
}
}

@@ -56,5 +67,5 @@ allprojects {

wrapper {
distributionType = Wrapper.DistributionType.BIN
gradleVersion = "8.1.1"
gradleVersion = "8.12.1"
}
27 changes: 10 additions & 17 deletions ejml-simple-bundle/build.gradle
Original file line number Diff line number Diff line change
@@ -5,10 +5,10 @@ plugins {
id 'signing'
}

def ejmlVersion = '0.41'
def ejmlVersion = '0.43.1'

java {
version = '0.41'
version = '0.43.1'
tasks.withType(Javadoc).all { enabled = false }
}

@@ -23,14 +23,14 @@ jar {
}

dependencies {
ejml 'org.ejml:ejml-simple:0.41'
ejml 'org.ejml:ejml-core:0.41'
ejml 'org.ejml:ejml-fdense:0.41'
ejml 'org.ejml:ejml-ddense:0.41'
ejml 'org.ejml:ejml-cdense:0.41'
ejml 'org.ejml:ejml-zdense:0.41'
ejml 'org.ejml:ejml-dsparse:0.41'
ejml 'org.ejml:ejml-fsparse:0.41'
ejml 'org.ejml:ejml-simple:' + ejmlVersion
ejml 'org.ejml:ejml-core:' + ejmlVersion
ejml 'org.ejml:ejml-fdense:' + ejmlVersion
ejml 'org.ejml:ejml-ddense:' + ejmlVersion
ejml 'org.ejml:ejml-cdense:' + ejmlVersion
ejml 'org.ejml:ejml-zdense:' + ejmlVersion
ejml 'org.ejml:ejml-dsparse:' + ejmlVersion
ejml 'org.ejml:ejml-fsparse:' + ejmlVersion
}

signing {
@@ -46,13 +46,6 @@ artifacts {
publishing {
repositories {
mavenLocal()
maven {
url = version.endsWith('SNAPSHOT') ? sonatypeSnapshotsUrl : sonatypeReleasesUrl
credentials {
username = ossrhUsername
password = ossrhPassword
}
}
}
publications {
mavenJava(MavenPublication) {
Loading