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.ml.clustering.assignment.hard; 031 032import org.openimaj.citation.annotation.Reference; 033import org.openimaj.citation.annotation.ReferenceType; 034import org.openimaj.ml.clustering.assignment.HardAssigner; 035import org.openimaj.util.pair.IntFloatPair; 036 037/** 038 * An assigner that wraps another hard assigner and only produces valid 039 * assignments if the closest cluster is within (or outside) of a given 040 * threshold distance. 041 * <p> 042 * Invalid assignments are marked by a cluster id of -1, and (if applicable) 043 * distance of {@link Float#NaN}. Users of this class must check the assignments 044 * and filter as necessary. 045 * 046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 047 * @param <DATATYPE> 048 * the primitive array datatype which represents a centroid of this 049 * cluster. 050 */ 051@Reference( 052 author = { "Y. Cai", "W. Tong", "L. Yang", "A. G. Hauptmann" }, 053 title = "Constrained Keypoint Quantization: Towards Better Bag-of-Words Model for Large-scale Multimedia Retrieval", 054 type = ReferenceType.Inproceedings, 055 year = "2012", 056 booktitle = "ACM International Conference on Multimedia Retrieval", 057 customData = { "location", "Hong Kong, China" }) 058public class ConstrainedFloatAssigner<DATATYPE> implements HardAssigner<DATATYPE, float[], IntFloatPair> { 059 HardAssigner<DATATYPE, float[], IntFloatPair> internalAssigner; 060 061 boolean allowIfGreater = false; 062 float threshold; 063 064 /** 065 * Construct the ConstrainedFloatAssigner with the given assigner and 066 * threshold. Assignments will be rejected if the distance given by the 067 * internal assigner are greater than the threshold. 068 * 069 * @param internalAssigner 070 * the internal assigner for computing distances. 071 * @param threshold 072 * the threshold at which assignments are rejected. 073 */ 074 public ConstrainedFloatAssigner(HardAssigner<DATATYPE, float[], IntFloatPair> internalAssigner, float threshold) { 075 this.threshold = threshold; 076 } 077 078 /** 079 * Construct the ConstrainedFloatAssigner with the given assigner and 080 * threshold. The greater flag determines if assignments should be rejected 081 * if the distance generated by the internal assigner is greater than the 082 * threshold (false) or less than the threshold (true). 083 * 084 * @param internalAssigner 085 * the internal assigner for computing distances. 086 * @param threshold 087 * the threshold at which assignments are rejected. 088 * @param greater 089 * if true distances less than the threshold are rejected; if 090 * false then distances greater than the threshold are rejected. 091 */ 092 public ConstrainedFloatAssigner(HardAssigner<DATATYPE, float[], IntFloatPair> internalAssigner, float threshold, 093 boolean greater) 094 { 095 this.allowIfGreater = greater; 096 this.threshold = threshold; 097 } 098 099 private boolean allow(float distance) { 100 if (allowIfGreater) { 101 return distance > threshold; 102 } 103 104 return distance < threshold; 105 } 106 107 @Override 108 public int[] assign(DATATYPE[] data) { 109 final int[] indices = new int[data.length]; 110 final float[] distances = new float[data.length]; 111 112 assignDistance(data, indices, distances); 113 114 return indices; 115 } 116 117 @Override 118 public int assign(DATATYPE data) { 119 return assignDistance(data).first; 120 } 121 122 @Override 123 public void assignDistance(DATATYPE[] data, int[] indices, float[] distances) { 124 internalAssigner.assignDistance(data, indices, distances); 125 126 for (int i = 0; i < data.length; i++) { 127 if (!allow(distances[i])) { 128 distances[i] = Float.NaN; 129 indices[i] = -1; 130 } 131 } 132 } 133 134 @Override 135 public IntFloatPair assignDistance(DATATYPE data) { 136 final IntFloatPair res = internalAssigner.assignDistance(data); 137 138 if (!allow(res.second)) { 139 res.second = Float.NaN; 140 res.first = -1; 141 } 142 143 return res; 144 } 145 146 @Override 147 public int size() { 148 return this.internalAssigner.size(); 149 } 150 151 @Override 152 public int numDimensions() { 153 return this.internalAssigner.numDimensions(); 154 } 155}