All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.raumzeitfalle.registration.displacement.Displacement Maven / Gradle / Ivy

Go to download

A collection of functions for image registration based on control points using rigid and affine transforms.

There is a newer version: 0.0.7
Show newest version
/*-
 * #%L
 * Image-Registration
 * %%
 * Copyright (C) 2019 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.displacement;

import java.util.Collection;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;

import net.raumzeitfalle.registration.alignment.TranslateFunction;

/**
 * Binds all details together, which are needed to calculate image registration for a single location. {@link Displacement} class is a data class. 
 * 

* Each displacement is bound to a specific location on a mask (x,y).
* The measured position of the structure is specified by (xd, yd).
* The differences between (x,y)d and (x,y) are calculated when a {@link Displacement} object is created.
*

* Depending of the location on a mask, a geometrical feature has a specific task. To distinguish between different features, the {@link Category} enum category type is used. * * @author Oliver Loeffler * */ public class Displacement { public static Displacement.Builder builder() { return new Displacement.Builder(); } /** * Creates a new {@link Displacement} object based on a given source object. * The actual displacement values (xd, yd) will be updated accordingly. * * @param source {@link Displacement} * @param xd - new displacement value for X-axis (design plus deviation) * @param yd - new displacement value for Y-axis (design plus deviation) * * @return {@link Displacement} */ public static Displacement from(Displacement source, double xd, double yd) { return new Displacement(source.index,source.id, source.x, source.y, xd, yd, source.category); } /** * Creates a new {@link Displacement} object from scratch, belonging to {@link Category} REG. * * @param index Usually a consecutive number (e.g. from a table) reflecting the order of {@link Displacement} object creation. * @param id An arbitrary ID value which can be assigned. * @param x Given design (reference) location (X-direction) for a displacement result. * @param y Given design (reference) location (Y-direction) for a displacement result. * * @return {@link Displacement} */ public static Displacement at(int index, int id, double x, double y) { return at(index,id, x, y, x, y, Category.REG); } /** * Creates a new {@link Displacement} object from scratch, belonging to {@link Category} REG. * * @param index Usually a consecutive number (e.g. from a table) reflecting the order of {@link Displacement} object creation. * @param id An arbitrary ID value which can be assigned. * @param x Given design (reference) location (X-direction) for a displacement result. * @param y Given design (reference) location (Y-direction) for a displacement result. * @param xd displaced (measured) X-coordinate * @param yd displaced (measured) Y-coordinate * * @return {@link Displacement} */ public static Displacement at(int index, int id, double x, double y, double xd, double yd) { return at(index,id, x, y, xd, yd, Category.REG); } /** * Creates a new {@link Displacement} object from scratch. * * @param index Usually a consecutive number (e.g. from a table) reflecting the order of {@link Displacement} object creation. * @param id An arbitrary ID value which can be assigned. * @param x Actual design location (X-direction) for a displacement result. * @param y Actual design location (Y-direction) for a displacement result. * @param xd displaced (measured) X-coordinate * @param yd displaced (measured) Y-coordinate * @param type Category type to distinguish different {@link Displacement} groups. * * @return {@link Displacement} */ public static Displacement at(int index, int id, double x, double y, double xd, double yd, Category type) { return new Displacement(index,id, x, y, xd, yd, type); } /** * Creates a statistical summary for all elements in the given collection of {@link Displacement} instances. * For both axes (X,Y) descriptive statistics such as average (mean), min, max, 3Sigma etc. are provided. * * @param t Given {@link Collection}ollection of {@link Displacement} items. * @return {@link DisplacementSummary} Table with descriptive statistics of positionals, alignment details (translation, rotation) and and first order systematics (scaling and shear aka. non-orthogonality). */ public static DisplacementSummary summarize(Collection t) { return DisplacementSummary.over(t, d->true); } /** * Creates a statistical summary for the given collection of {@link Displacement} instances. * For both axes (X,Y) descriptive statistics such as average (mean), min, max, 3Sigma etc. are provided. * * @param t Given {@link Collection}ollection of {@link Displacement} items. * @param calculationSelection {@link Predicate} for {@link Displacement} which determines, which Displacements will be considered for summary creation. * @return {@link DisplacementSummary} Table with descriptive statistics of positionals, alignment details (translation, rotation) and and first order systematics (scaling and shear aka. non-orthogonality). */ public static DisplacementSummary summarize(Collection t, Predicate calculationSelection) { return DisplacementSummary.over(t, calculationSelection); } /** * Creates the average value for a given double property of a {@link Displacement} while considering only Displacements which match the filter {@link Predicate}. * * @param t Given {@link Collection}ollection of {@link Displacement} items. * @param filter {@link Predicate} to {@link Displacement} which determines which Displacements will be considered for averaging. * @param mapper {@link ToDoubleFunction} which extracts a double value from a {@link Displacement} * @return double */ public static double average(Collection t, Predicate filter, ToDoubleFunction mapper) { return t.stream() .filter(filter) .mapToDouble(mapper) .filter(Double::isFinite) .average() .orElse(Double.NaN); } public static TranslateFunction translationToCenter(Collection displacements, Predicate u) { double meanx = Displacement.average(displacements, u, Displacement::getX); double meany = Displacement.average(displacements, u, Displacement::getY); return new TranslateFunction(-meanx, -meany); } private final int index; private final int id; private final double x; private final double y; private final double xd; private final double yd; private final double dx; private final double dy; private final Category category; private Displacement(int index, int id, double x, double y, double xd, double yd, Category type) { this.index = index; this.id = id; this.x = x; this.y = y; this.xd = xd; this.yd = yd; this.dx = xd -x; this.dy = yd -y; this.category = Objects.requireNonNull(type, "type must not be null"); } /** * Moves a displacement to a new location by adding an offsetX and offsetY to given design coordinates (x,y) and to displaced coordinates (xd,yd). * * @param offsetX Offset for X direction, will be applied to x, xd. * @param offsetY Offset for Y direction, will be applied to y, yd. * * @return Displacement where: *

    *
  • xmoved = xold + offsetX *
  • ymoved = yold + offsetY *
  • xdmoved = xdold + offsetX *
  • ydmoved = ydold + offsetY *
*/ public Displacement moveBy(double offsetX, double offsetY) { return new Displacement(index, id, offsetX + x, offsetY + y, offsetX + xd, offsetY + yd, category); } /** * Corrects (xd,yd) by subtracting the corresponding (dx,dy) whereas the design coordinates (x,y) remain unmodified. * * @param dx Difference in X direction to be subtracted from xd. * @param dy Difference in Y direction to be subtracted from yd. * @return Displacement where: *
    *
  • xdcorrected = xdold - dx *
  • ydcorrected = ydold - dy *
*/ public Displacement correctBy(double dx, double dy) { return new Displacement(index, id, x, y, xd - dx, yd - dy, category); } public int getIndex() { return index; } public int getId() { return id; } /** * Provides the X component of design coordinate. * @return double x */ public double getX() { return x; } /** * Provides the Y component of design coordinate. * @return double y */ public double getY() { return y; } /** * Provides the X component of displaced (measured) coordinate. * @return double xd */ public double getXd() { return xd; } /** * Provides the Y component of displaced (measured) coordinate. * @return double yd */ public double getYd() { return yd; } /** * Difference dx = xd - x * @return double dx */ public double dX() { return dx; } /** * Difference dy = yd - y * @return double dy */ public double dY() { return dy; } /** * Provides a category description for each {@link Displacement}. * @return {@link Category} */ public Category getCategory() { return category; } @Override public String toString() { return "Displacement [type="+category.name()+ " id=" + id + " x=" + x + ", y=" + y + ", xd=" + xd + ", yd=" + yd + ", " + System.lineSeparator() + "\t\tdx=" + dx + ", dy=" + dy + "]"; } /** * Verifies if this {@link Displacement} belongs to a specific {@link Category}. * @param other {@link Category} to be tested for * @return true, when this {@link Displacement} belongs to the provided {@link Category}. */ public boolean belongsTo(Category other) { return this.category.equals(other); } public static class Builder implements Supplier { private Integer index = null; private Integer id = null; private Double x = null; private Double y = null; private Double xd = null; private Double yd = null; private Category category = Category.REG; public Builder withIndex(Integer index) { Objects.requireNonNull(index, "Index must not be null"); this.index = index; return this; } public Builder withId(Integer id) { Objects.requireNonNull(id, "ID must not be null"); this.id = id; return this; } public Builder ofCategory(Category category) { Objects.requireNonNull(category, "Category must not be null"); this.category = category; return this; } public Builder atLocation(Double refx, Double refy) { Objects.requireNonNull(refx, "refx must not be null"); Objects.requireNonNull(refy, "refy must not be null"); this.x = refx; this.y = refy; return this; } public Builder displacedTo(Double xd, Double yd) { Objects.requireNonNull(xd, "xd must not be null"); Objects.requireNonNull(yd, "yd must not be null"); this.xd = xd; this.yd = yd; return this; } public Builder xDisplacedTo(Double xd) { Objects.requireNonNull(xd, "xd must not be null"); this.xd = xd; return this; } public Builder yDisplacedTo(Double yd) { Objects.requireNonNull(yd, "yd must not be null"); this.yd = yd; return this; } public Builder withXDeviation(Double deltaX) { Objects.requireNonNull(deltaX, "deltaX must not be null"); this.xd = deltaX + this.x; return this; } public Builder withYDeviation(Double deltaY) { Objects.requireNonNull(deltaY, "deltaY must not be null"); this.yd = deltaY + this.y; return this; } public Displacement build() { Objects.requireNonNull(index, "Index must not be null. Displacement Builder is not properly initialized."); if (null == id) id = index; Objects.requireNonNull(x, "x must not be null."); Objects.requireNonNull(y, "y must not be null."); Objects.requireNonNull(xd, "xd must not be null."); Objects.requireNonNull(yd, "yd must not be null."); return Displacement.at(index, id, x, y, xd, yd, category); } @Override public Displacement get() { return build(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy