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.hadoop.tools.localfeature;
031
032import java.io.ByteArrayOutputStream;
033import java.io.IOException;
034import java.io.InputStream;
035import java.util.HashMap;
036import java.util.Map;
037
038import org.apache.hadoop.conf.Configured;
039import org.apache.hadoop.fs.Path;
040import org.apache.hadoop.io.BytesWritable;
041import org.apache.hadoop.io.Text;
042import org.apache.hadoop.mapreduce.Job;
043import org.apache.hadoop.mapreduce.Mapper;
044import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
045import org.apache.hadoop.util.Tool;
046import org.apache.hadoop.util.ToolRunner;
047import org.apache.log4j.Logger;
048import org.openimaj.feature.local.LocalFeature;
049import org.openimaj.feature.local.list.LocalFeatureList;
050import org.openimaj.hadoop.mapreduce.TextBytesJobUtil;
051import org.openimaj.hadoop.sequencefile.MetadataConfiguration;
052import org.openimaj.hadoop.sequencefile.TextBytesSequenceFileUtility;
053import org.openimaj.io.IOUtils;
054import org.openimaj.time.Timer;
055
056/**
057 * Hadoop version of the LocalFeaturesTool. Capable of extracting features from
058 * images in sequencefiles.
059 * 
060 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
061 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
062 */
063public class HadoopLocalFeaturesTool extends Configured implements Tool {
064        private static final String ARGS_KEY = "openimaj.localfeaturestool.args";
065
066        /**
067         * Feature extraction mapper.
068         * 
069         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
070         * @author Sina Samangooei (ss@ecs.soton.ac.uk)
071         */
072        static class LocalFeaturesMapper extends Mapper<Text, BytesWritable, Text, BytesWritable> {
073                static enum Counters {
074                        SUCCESSFUL, FAILED;
075                }
076
077                private static final Logger logger = Logger.getLogger(LocalFeaturesMapper.class);
078                private HadoopLocalFeaturesToolOptions options;
079
080                @Override
081                protected void setup(Context context) throws IOException,
082                                InterruptedException
083                {
084                        final InputStream ios = null;
085                        try {
086                                options = new HadoopLocalFeaturesToolOptions(context.getConfiguration().getStrings(ARGS_KEY));
087                                options.prepare();
088                        } finally {
089                                if (ios != null)
090                                        ios.close();
091                        }
092                }
093
094                @Override
095                protected void
096                                map(Text key, BytesWritable value, Context context) throws IOException, InterruptedException
097                {
098                        try {
099                                final Timer t = Timer.timer();
100                                logger.info("Generating Keypoint for image: " + key);
101                                logger.trace("Keypoint mode: " + options.getMode());
102                                ByteArrayOutputStream baos = null;
103                                final LocalFeatureList<? extends LocalFeature<?, ?>> kpl = options.getMode().extract(value.getBytes());
104
105                                logger.debug("Keypoints generated! Found: " + kpl.size());
106                                if (options.dontwrite) {
107                                        logger.trace("Not Writing");
108                                        return;
109                                }
110
111                                logger.trace("Writing");
112                                baos = new ByteArrayOutputStream();
113                                if (options.isAsciiMode()) {
114                                        IOUtils.writeASCII(baos, kpl);
115                                } else {
116                                        IOUtils.writeBinary(baos, kpl);
117                                }
118                                context.write(key, new BytesWritable(baos.toByteArray()));
119                                logger.info("Done in " + t.duration() + "ms");
120                                context.getCounter(Counters.SUCCESSFUL).increment(1L);
121                        } catch (final Throwable e) {
122                                context.getCounter(Counters.FAILED).increment(1L);
123                                logger.warn("Problem with this image. (" + e + "/" + key + ")");
124                                e.printStackTrace(System.err);
125                        }
126                }
127        }
128
129        @Override
130        public int run(String[] args) throws Exception {
131                final HadoopLocalFeaturesToolOptions options = new HadoopLocalFeaturesToolOptions(args, true);
132                options.prepare();
133
134                final Path[] paths = options.getInputPaths();
135                final TextBytesSequenceFileUtility util = new TextBytesSequenceFileUtility(paths[0].toUri(), true);
136                final Map<String, String> metadata = new HashMap<String, String>();
137
138                if (util.getUUID() != null)
139                        metadata.put(MetadataConfiguration.UUID_KEY, util.getUUID());
140
141                metadata.put(MetadataConfiguration.CONTENT_TYPE_KEY, "application/localfeatures-" + options.getMode().name()
142                                + "-" + (options.isAsciiMode() ? "ascii" : "bin"));
143
144                final Job job = TextBytesJobUtil.createJob(paths, options.getOutputPath(), metadata, this.getConf());
145                job.setJarByClass(this.getClass());
146
147                options.mapperModeOp.prepareJobMapper(job, LocalFeaturesMapper.class);
148                job.getConfiguration().setStrings(ARGS_KEY, args);
149                job.setNumReduceTasks(0);
150
151                SequenceFileOutputFormat.setCompressOutput(job, !options.dontcompress);
152
153                long start, end;
154                start = System.currentTimeMillis();
155                job.waitForCompletion(true);
156                end = System.currentTimeMillis();
157
158                System.out.println("Took: " + (end - start) + "ms");
159
160                options.serialiseExtractor();
161
162                return 0;
163        }
164
165        /**
166         * The main entry point
167         * 
168         * @param args
169         * @throws Exception
170         */
171        public static void main(String[] args) throws Exception {
172                ToolRunner.run(new HadoopLocalFeaturesTool(), args);
173        }
174}