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}