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.demos.sandbox.siftlike; 031 032import java.io.File; 033import java.io.IOException; 034import java.util.ArrayList; 035import java.util.HashMap; 036import java.util.List; 037import java.util.Map; 038import java.util.Set; 039 040import org.openimaj.feature.OrientedFeatureVector; 041import org.openimaj.image.DisplayUtilities; 042import org.openimaj.image.FImage; 043import org.openimaj.image.ImageUtilities; 044import org.openimaj.image.pixel.ConnectedComponent; 045import org.openimaj.image.pixel.Pixel; 046import org.openimaj.image.processing.edges.StrokeWidthTransform; 047import org.openimaj.math.geometry.shape.Ellipse; 048import org.openimaj.math.geometry.shape.EllipseUtilities; 049import org.openimaj.util.set.DisjointSetForest; 050 051import Jama.Matrix; 052 053public class SWTSIFT { 054 public static void main(String[] args) throws IOException { 055 final FImage image = ImageUtilities 056 .readF(new File( 057 "/Users/jon/Library/Containers/com.apple.mail/Data/Library/Mail Downloads/6244F204-C7AC-441C-853E-51B0E1D722D6/standard-conference-name-badge-holder.png")); 058 059 image.processInplace(new StrokeWidthTransform(true, 1f)); 060 061 StrokeWidthTransform.normaliseImage(image); 062 063 DisplayUtilities.display(image); 064 065 // FImage img = ImageUtilities 066 // .readF(new File( 067 // "/Users/jon/Library/Containers/com.apple.mail/Data/Library/Mail Downloads/4375E35B-F73B-4FF4-B45A-2E2A8D05A62D/example images/typical examples/G0020428.JPG")); 068 // 069 // // img = ResizeProcessor.halfSize(img); 070 // img = img.extractROI(800, 200, 1000, 400); 071 // 072 // DisplayUtilities.display(img); 073 // final StrokeWidthTransform swt = new StrokeWidthTransform(false, 074 // 1.f); 075 // img.processInplace(swt); 076 // DisplayUtilities.display(StrokeWidthTransform.normaliseImage(img)); 077 078 // final FImage logo = ImageUtilities.readF(new URL( 079 // "http://www.linkshop.com.cn/upload/joinbook/2011/201183951496638.jpg")).inverse(); 080 // 081 // final Map<Ellipse, OrientedFeatureVector> im1f = 082 // extractFeatures(img); 083 // final Map<Ellipse, OrientedFeatureVector> im2f = 084 // extractFeatures(logo); 085 // 086 // final MBFImage tmp = new MBFImage(img.width + logo.width, 087 // Math.max(img.height, logo.height)); 088 // tmp.drawImage(img.toRGB(), 0, 0); 089 // tmp.drawImage(logo.toRGB(), img.width, 0); 090 // 091 // for (final Entry<Ellipse, OrientedFeatureVector> i1 : 092 // im1f.entrySet()) { 093 // double minDistance = Double.MAX_VALUE, minDistance2 = 094 // Double.MAX_VALUE; 095 // Ellipse bestEllipse = null; 096 // for (final Entry<Ellipse, OrientedFeatureVector> i2 : 097 // im2f.entrySet()) { 098 // final double distance = i1.getValue().compare(i2.getValue(), 099 // ByteFVComparison.EUCLIDEAN); 100 // if (distance < minDistance) { 101 // minDistance2 = minDistance; 102 // minDistance = distance; 103 // bestEllipse = i2.getKey(); 104 // } else if (distance < minDistance2) { 105 // minDistance2 = distance; 106 // } 107 // } 108 // 109 // if (minDistance < 0.9 * minDistance2) { 110 // tmp.drawShape(i1.getKey(), RGBColour.RED); 111 // final Ellipse be = bestEllipse.clone(); 112 // be.translate(img.width, 0); 113 // tmp.drawShape(be, RGBColour.RED); 114 // 115 // tmp.drawLine(i1.getKey().calculateCentroid(), be.calculateCentroid(), 116 // RGBColour.BLUE); 117 // } 118 // } 119 // DisplayUtilities.display(tmp); 120 } 121 122 private static Map<Ellipse, OrientedFeatureVector> extractFeatures(FImage img) { 123 final StrokeWidthTransform swt = new StrokeWidthTransform(false, 1.0f); 124 final FImage swtImage = img.process(swt); 125 DisplayUtilities.display(StrokeWidthTransform.normaliseImage(swtImage)); 126 127 final EllipseGradientFeatureExtractor egfe = new EllipseGradientFeatureExtractor(); 128 129 final List<ConnectedComponent> ccs = findComponents(swtImage); 130 final Map<Ellipse, OrientedFeatureVector> im1f = new HashMap<Ellipse, OrientedFeatureVector>(); 131 for (final ConnectedComponent cc : ccs) { 132 final double[] centroid = cc.calculateCentroid(); 133 final Matrix m = computeCovariance(cc, centroid); 134 135 final Ellipse e = EllipseUtilities.ellipseFromCovariance((float) centroid[0], (float) centroid[1], m, 3f); 136 137 for (final OrientedFeatureVector f : egfe.extract(img, e)) 138 im1f.put(e, f); 139 } 140 141 return im1f; 142 } 143 144 private static Matrix computeCovariance(ConnectedComponent cc, double[] centroid) { 145 final Matrix m = new Matrix(2, 2); 146 final double[][] md = m.getArray(); 147 148 for (final Pixel p : cc) { 149 md[0][0] += ((p.x - centroid[0]) * (p.x - centroid[0])); 150 md[1][1] += ((p.y - centroid[1]) * (p.y - centroid[1])); 151 md[0][1] += ((p.x - centroid[0]) * (p.y - centroid[1])); 152 } 153 154 final int area = cc.calculateArea(); 155 md[0][0] /= area; 156 md[1][1] /= area; 157 md[0][1] /= area; 158 md[1][0] = md[0][1]; 159 160 return m; 161 } 162 163 private final static int[][] connect8 = { 164 { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 }, { -1, -1 }, { 1, -1 }, { -1, 1 }, { 1, 1 } }; 165 166 static List<ConnectedComponent> findComponents(FImage image) { 167 final DisjointSetForest<Pixel> forest = new DisjointSetForest<Pixel>(); 168 169 Pixel current = new Pixel(); 170 Pixel next = new Pixel(); 171 for (int y = 0; y < image.height; y++) { 172 for (int x = 0; x < image.width; x++) { 173 final float currentValue = image.pixels[y][x]; 174 175 if (currentValue > 0 && currentValue != Float.POSITIVE_INFINITY) { 176 current.x = x; 177 current.y = y; 178 179 if (forest.makeSet(current) != null) 180 current = current.clone(); 181 182 for (int i = 0; i < connect8.length; i++) { 183 final int xx = x + connect8[i][0]; 184 final int yy = y + connect8[i][1]; 185 186 if (xx >= 0 && xx < image.width - 1 && yy >= 0 && yy < image.height - 1) { 187 final float value = image.pixels[yy][xx]; 188 189 if (value > 0 && value != Float.POSITIVE_INFINITY) { 190 next.x = xx; 191 next.y = yy; 192 193 if (forest.makeSet(next) != null) 194 next = next.clone(); 195 196 // if (Math.max(currentValue, value) / 197 // Math.min(currentValue, value) < 3) 198 forest.union(current, next); 199 } 200 } 201 } 202 } 203 } 204 } 205 206 final List<ConnectedComponent> components = new ArrayList<ConnectedComponent>(); 207 for (final Set<Pixel> pixels : forest.getSubsets()) { 208 components.add(new ConnectedComponent(pixels)); 209 } 210 211 return components; 212 } 213}