001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.math.geometry.transforms; 031 032import java.util.List; 033 034import org.openimaj.math.geometry.point.Point2d; 035import org.openimaj.math.matrix.MatrixUtils; 036import org.openimaj.math.model.EstimatableModel; 037import org.openimaj.util.function.Predicate; 038import org.openimaj.util.pair.IndependentPair; 039import org.openimaj.util.pair.Pair; 040 041import Jama.Matrix; 042 043/** 044 * Implementation of a Homogeneous Homography model - a transform that models 045 * the relationship between planes under projective constraints (8 D.o.F) 046 * 047 * @author Jonathon Hare 048 * 049 */ 050public class HomographyModel implements EstimatableModel<Point2d, Point2d>, MatrixTransformProvider { 051 protected Predicate<HomographyModel> modelCheck; 052 protected Matrix homography = Matrix.identity(3, 3); 053 protected boolean normalise; 054 055 /** 056 * Create an {@link HomographyModel} that automatically normalises the data 057 * given to {@link #estimate(List)} to get a numerically stable estimate. 058 */ 059 public HomographyModel() 060 { 061 this(true); 062 } 063 064 /** 065 * Create a {@link HomographyModel} with optional automatic normalisation. 066 * 067 * @param norm 068 * true if the data should be automatically normalised before 069 * running the DLT algorithm 070 */ 071 public HomographyModel(boolean norm) 072 { 073 this.normalise = norm; 074 } 075 076 /** 077 * Create an {@link HomographyModel} that automatically normalises the data 078 * given to {@link #estimate(List)} to get a numerically stable estimate. 079 * The given {@link Predicate} is used by the {@link #estimate(List)} method 080 * to test whether the estimated homography is sensible. 081 * 082 * @param mc 083 * the test function for sensible homographies 084 */ 085 public HomographyModel(Predicate<HomographyModel> mc) 086 { 087 this(true, mc); 088 } 089 090 /** 091 * Create a {@link HomographyModel} with optional automatic normalisation. 092 * The given {@link Predicate} is used by the {@link #estimate(List)} method 093 * to test whether the estimated homography is sensible 094 * 095 * @param norm 096 * true if the data should be automatically normalised before 097 * running the DLT algorithm 098 * @param mc 099 * the test function for sensible homographies 100 */ 101 public HomographyModel(boolean norm, Predicate<HomographyModel> mc) 102 { 103 this.normalise = norm; 104 this.modelCheck = mc; 105 } 106 107 @Override 108 public HomographyModel clone() { 109 final HomographyModel hm = new HomographyModel(normalise); 110 hm.homography = homography.copy(); 111 return hm; 112 } 113 114 @Override 115 public Matrix getTransform() { 116 return homography; 117 } 118 119 /** 120 * Set the transform matrix to the new one 121 * 122 * @param matrix 123 * the new matrix 124 */ 125 public void setTransform(Matrix matrix) { 126 homography = matrix; 127 } 128 129 /** 130 * DLT estimation of least-squares solution of 3D homogeneous homography 131 * 132 * @see org.openimaj.math.model.EstimatableModel#estimate(java.util.List) 133 */ 134 @Override 135 public boolean estimate(List<? extends IndependentPair<Point2d, Point2d>> data) { 136 if (normalise) { 137 homography = TransformUtilities.homographyMatrixNorm(data); 138 } else { 139 homography = TransformUtilities.homographyMatrix(data); 140 } 141 if (this.modelCheck == null) 142 return true; 143 144 return this.modelCheck.test(this); 145 } 146 147 /** 148 * De-normalise a homography estimate. Use when {@link #estimate(List)} was 149 * called with pre-normalised data. 150 * 151 * @param normalisations 152 * the normalisation transforms 153 */ 154 public void denormaliseHomography(Pair<Matrix> normalisations) { 155 homography = normalisations.secondObject().inverse().times(homography).times(normalisations.firstObject()); 156 if (Math.abs(homography.get(2, 2)) > 0.000001) { 157 MatrixUtils.times(homography, 1.0 / homography.get(2, 2)); 158 } 159 } 160 161 @Override 162 public int numItemsToEstimate() { 163 return 4; 164 } 165 166 @Override 167 public Point2d predict(Point2d data) { 168 return data.transform(homography); 169 } 170}