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}