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.tools.globalfeature;
031
032import java.util.Arrays;
033
034import org.kohsuke.args4j.CmdLineException;
035import org.kohsuke.args4j.CmdLineParser;
036import org.kohsuke.args4j.MBFImageOptionHandler;
037import org.kohsuke.args4j.Option;
038import org.kohsuke.args4j.ProxyOptionHandler;
039import org.openimaj.feature.ByteFV;
040import org.openimaj.feature.ByteFVComparison;
041import org.openimaj.feature.DoubleFV;
042import org.openimaj.feature.DoubleFVComparison;
043import org.openimaj.feature.FVComparator;
044import org.openimaj.feature.FeatureVector;
045import org.openimaj.feature.FloatFV;
046import org.openimaj.feature.FloatFVComparison;
047import org.openimaj.feature.IntFV;
048import org.openimaj.feature.IntFVComparison;
049import org.openimaj.feature.ShortFV;
050import org.openimaj.feature.ShortFVComparison;
051import org.openimaj.image.FImage;
052import org.openimaj.image.MBFImage;
053import org.openimaj.image.analysis.algorithm.FloodFill;
054import org.openimaj.tools.globalfeature.ShapeFeatures.ShapeFeaturesOp;
055
056/**
057 * A tool for computing the similarities/distances between two images based
058 * on a feature from the foreground object in the image. The flood-fill algorithm is
059 * used to segment the foreground/background based 
060 * on a seed pixel and distance threshold.
061 * 
062 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
063 */
064public class SegmentingPairWiseComparisonTool {
065        @Option(name="--image-1", aliases="-im1", usage="first image", handler=MBFImageOptionHandler.class, required=true)
066        private MBFImage im1;
067        
068        @Option(name="--image-2", aliases="-im2", usage="second image", handler=MBFImageOptionHandler.class, required=true)
069        private MBFImage im2;
070        
071        @Option(name="--metric", aliases="-m", usage="comparison metric", required=true)
072        private FeatureComparison compare;
073        
074        @Option(name="--feature-type", aliases="-f", handler=ProxyOptionHandler.class, usage="Feature type", required=true)
075        private ShapeFeatures feature;
076        private ShapeFeaturesOp featureOp;
077        
078        @Option(name = "--px1", aliases="-px1", required=false, usage="x-position of the starting pixel in image 1")
079        private int px1 = 0;
080        
081        @Option(name = "--py1", aliases="-py1", required=false, usage="y-position of the starting pixel in image 1")
082        private int py1 = 0;
083        
084        @Option(name = "--thresh1", aliases="-thresh1", required=false, usage="threshold for flood-fill algorithm in image 1")
085        private float thresh1 = 25F/255F;
086        
087        @Option(name = "--px2", aliases="-px2", required=false, usage="x-position of the starting pixel in image 2")
088        private int px2 = 0;
089        
090        @Option(name = "--py2", aliases="-py2", required=false, usage="y-position of the starting pixel in image 2")
091        private int py2 = 0;
092        
093        @Option(name = "--thresh2", aliases="-thresh2", required=false, usage="threshold for flood-fill algorithm in image 2")
094        private float thresh2 = 25F/255F;
095        
096        
097        @SuppressWarnings("unchecked")
098        protected <T extends FeatureVector> FVComparator<T> getComp(T fv, FeatureComparison type) {
099                if (fv instanceof ByteFV) return (FVComparator<T>) ByteFVComparison.valueOf(type.name());
100                if (fv instanceof ShortFV) return (FVComparator<T>) ShortFVComparison.valueOf(type.name());
101                if (fv instanceof IntFV) return (FVComparator<T>) IntFVComparison.valueOf(type.name());
102                if (fv instanceof FloatFV) return (FVComparator<T>) FloatFVComparison.valueOf(type.name());
103                if (fv instanceof DoubleFV) return (FVComparator<T>) DoubleFVComparison.valueOf(type.name());
104                return null;
105        }
106        
107        double execute() {
108                FImage mask1 = FloodFill.floodFill(im1, px1, py1, thresh1);
109                FImage mask2 = FloodFill.floodFill(im2, px2, py2, thresh2);
110                
111                FeatureVector fv1 = featureOp.execute(im1, mask1);
112                FeatureVector fv2 = featureOp.execute(im2, mask2);
113                
114                if (compare == FeatureComparison.EQUALS) {
115                        if (Arrays.equals(fv1.asDoubleVector(), fv2.asDoubleVector()))
116                                return 1;
117                        return 0;
118                } else {
119                        return getComp(fv1, compare).compare(fv1, fv2);
120                }
121        }
122        
123        /**
124         * The main method of the tool.
125         * @param args
126         */
127        public static void main(String [] args) {
128                SegmentingPairWiseComparisonTool tool = new SegmentingPairWiseComparisonTool();
129                
130                CmdLineParser parser = new CmdLineParser(tool);
131
132                try {
133                        parser.parseArgument(args);
134                } catch (CmdLineException e) {
135                        System.err.println(e.getMessage());
136                        System.err.println("Usage: pairwisecomp [options...]");
137                        parser.printUsage(System.err);
138                        
139                        if (tool.feature == null) {
140                                for (GlobalFeatureType m : GlobalFeatureType.values()) {
141                                        System.err.println();
142                                        System.err.println(m + " options: ");
143                                        new CmdLineParser(m.getOptions()).printUsage(System.err);
144                                }
145                        }
146                        return;
147                }
148                
149                System.out.println(tool.execute());
150        }
151}