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.clusterquantiser; 031 032import java.io.BufferedReader; 033import java.io.File; 034import java.io.FileInputStream; 035import java.io.FileNotFoundException; 036import java.io.FileReader; 037import java.io.IOException; 038import java.io.ObjectInputStream; 039import java.util.ArrayList; 040import java.util.List; 041 042import org.kohsuke.args4j.CmdLineException; 043import org.kohsuke.args4j.Option; 044import org.kohsuke.args4j.ProxyOptionHandler; 045import org.openimaj.ml.clustering.SpatialClusters; 046import org.openimaj.tools.clusterquantiser.ClusterType.ClusterTypeOp; 047import org.openimaj.util.array.ByteArrayConverter; 048 049/** 050 * Options for {@link ClusterQuantiser} tool. 051 * 052 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 053 * 054 */ 055public class ClusterQuantiserOptions extends AbstractClusterQuantiserOptions { 056 057 @Option( 058 name = "--print-time-taken", 059 aliases = "-ptt", 060 required = false, 061 usage = "Print to the standard output the time taken to extract features") 062 boolean printTime = false; 063 064 /** 065 * Construct with arguments 066 * 067 * @param args 068 */ 069 public ClusterQuantiserOptions(String[] args) { 070 super(args); 071 } 072 073 /** 074 * Construct with defaults 075 */ 076 public ClusterQuantiserOptions() { 077 super(null); 078 } 079 080 @Option( 081 name = "--create", 082 aliases = "-c", 083 required = false, 084 usage = "Create a new vocabulary and save as FILE.", 085 metaVar = "String ") 086 private String createFile; 087 private boolean create_mode = false; 088 089 @Option( 090 name = "--batched-samples", 091 aliases = "-bs", 092 required = false, 093 usage = "Batched sample mode.", 094 metaVar = "BOOLEAN") 095 private boolean batchedSampleMode = false; 096 097 @Option( 098 name = "--cluster-type", 099 aliases = "-ct", 100 required = false, 101 usage = "Specify the type of file to be read.", 102 handler = ProxyOptionHandler.class) 103 private ClusterType clusterType = ClusterType.HKMEANS; 104 protected ClusterTypeOp clusterTypeOp = (ClusterTypeOp) ClusterType.HKMEANS.getOptions(); 105 106 protected Class<? extends SpatialClusters<?>> clusterClass = clusterTypeOp.getClusterClass(); 107 protected Class<? extends SpatialClusters<?>> otherClusterClass = clusterTypeOp.getClusterClass(); 108 109 @Option( 110 name = "--samples", 111 aliases = "-s", 112 required = false, 113 usage = "Use NUMBER samples from the input.", 114 metaVar = "NUMBER") 115 private int samples = -1; 116 117 @Option( 118 name = "--samples-file", 119 aliases = "-sf", 120 required = false, 121 usage = "Save the samples to a file. Load them from this file if it exists", 122 metaVar = "FILE") 123 protected File samplesFile = null; 124 protected boolean samplesFileMode = false; 125 private byte[][] sampleKeypoints = null; 126 127 @Option( 128 name = "--input-file", 129 aliases = "-f", 130 required = false, 131 usage = "Read the input from those specified in FILE.", 132 metaVar = "FILE") 133 protected File input_file = null; 134 135 @Option( 136 name = "--output-folder", 137 aliases = "-o", 138 required = false, 139 usage = "Where to output all the quantised loc files", 140 metaVar = "FILE") 141 private File output_file = null; 142 private ClusterTypeOp otherClusterType; 143 144 /** 145 * @return true if using a samples file 146 */ 147 public boolean isSamplesFileMode() { 148 return samplesFileMode; 149 } 150 151 /** 152 * @return the sample points 153 */ 154 public byte[][] getSampleKeypoints() { 155 return sampleKeypoints; 156 } 157 158 /** 159 * @return number of samples 160 */ 161 public int getSamples() { 162 return samples; 163 } 164 165 /** 166 * @return the samples file 167 */ 168 public File getSamplesFile() { 169 return this.samplesFile; 170 } 171 172 @Override 173 public ClusterTypeOp getClusterType() { 174 return this.clusterTypeOp; 175 } 176 177 /** 178 * load the samples file 179 */ 180 public void loadSamplesFile() { 181 if (this.sampleKeypoints != null) 182 return; 183 184 System.err.println("Loading samples file..."); 185 ObjectInputStream ois = null; 186 try { 187 ois = new ObjectInputStream(new FileInputStream(this.getSamplesFile())); 188 final Object read = ois.readObject(); 189 if (read instanceof byte[][]) { 190 this.sampleKeypoints = (byte[][]) read; 191 } else { 192 this.sampleKeypoints = ByteArrayConverter.intToByte((int[][]) read); 193 } 194 } catch (final FileNotFoundException e) { 195 // TODO Auto-generated catch block 196 e.printStackTrace(); 197 } catch (final IOException e) { 198 // TODO Auto-generated catch block 199 e.printStackTrace(); 200 } catch (final ClassNotFoundException e) { 201 // TODO Auto-generated catch block 202 e.printStackTrace(); 203 } finally { 204 try { 205 ois.close(); 206 } catch (final IOException e) { 207 } 208 } 209 } 210 211 @Override 212 public void validate() throws CmdLineException { 213 if (createFile != null) { 214 create_mode = true; 215 } 216 if (infoFile != null) { 217 if (create_mode) 218 throw new CmdLineException(null, 219 "--info and --create are mutually exclusive."); 220 this.clusterTypeOp = ClusterType.sniffClusterType(new File(infoFile)); 221 this.clusterClass = this.clusterTypeOp.getClusterClass(); 222 223 if (otherInfoFile != null) { 224 this.otherClusterType = ClusterType.sniffClusterType(new File(otherInfoFile)); 225 this.otherClusterClass = this.otherClusterType.getClusterClass(); 226 } 227 info_mode = true; 228 } 229 File quantFile = null; 230 if (quantLocation != null) { 231 quantFile = new File(quantLocation); 232 233 if (create_mode) 234 throw new CmdLineException(null, "--quant and --create are mutually exclusive."); 235 if (info_mode) 236 throw new CmdLineException(null, "--quant and --info are mutually exclusive."); 237 238 quant_mode = true; 239 this.clusterTypeOp = ClusterType.sniffClusterType(quantFile); 240 this.clusterClass = this.clusterTypeOp.getClusterClass(); 241 } 242 if (samplesFile != null && samplesFile.exists()) { 243 samplesFileMode = true; 244 if (!this.batchedSampleMode) 245 loadSamplesFile(); 246 } 247 248 if (!create_mode && !info_mode && !quant_mode && samplesFile == null) { 249 throw new CmdLineException(null, ""); 250 } 251 252 if (samplesFile == null && !info_mode && fileType == null) { 253 throw new CmdLineException( 254 null, 255 "--file-type must be specified with --create and --quant arguments. Or you must provied a --samples-file"); 256 } 257 258 if (input_file != null && inputFiles.size() > 0) 259 throw new CmdLineException( 260 null, 261 "Input files from the commandline arguments not supported with --input-file argument."); 262 if (input_file != null && input_file.exists() != true) { 263 throw new CmdLineException( 264 null, 265 "--input-file input source does not exist"); 266 } 267 if (this.getCountMode()) { 268 if (this.extension.equals(".loc")) 269 this.extension = ".counts"; 270 } 271 } 272 273 @Override 274 public String getTreeFile() throws IOException { 275 if (create_mode) { 276 final File createFileParent = new File(createFile).getAbsoluteFile().getParentFile(); 277 if (!createFileParent.exists()) { 278 if (!createFileParent.mkdirs()) { 279 throw new IOException("Invalid quant file"); 280 } 281 } else { 282 if (!createFileParent.isDirectory()) 283 throw new IOException("Invalid quant file"); 284 } 285 return createFile; 286 } 287 return super.getTreeFile(); 288 } 289 290 /** 291 * @return true if in create mode 292 */ 293 public boolean isCreateMode() { 294 return create_mode; 295 } 296 297 /** 298 * @return true if using batched samples 299 */ 300 public boolean isBatchedSampleMode() { 301 return this.batchedSampleMode; 302 } 303 304 /** 305 * @return the input files 306 * @throws IOException 307 */ 308 public List<File> getInputFiles() throws IOException { 309 final List<File> files = new ArrayList<File>(); 310 311 if (inputFiles.size() > 0) { 312 return inputFiles; 313 } else if (input_file != null) { 314 BufferedReader br = null; 315 try { 316 br = new BufferedReader(new FileReader(input_file)); 317 318 String line; 319 while ((line = br.readLine()) != null) { 320 files.add(new File(line.trim())); 321 } 322 } finally { 323 try { 324 br.close(); 325 } catch (final IOException e) { 326 e.printStackTrace(); 327 } 328 } 329 } 330 331 return files; 332 } 333 334 /** 335 * @return the output file 336 * @throws IOException 337 */ 338 public synchronized File getOutputFile() throws IOException { 339 if (this.output_file != null) { 340 if (!output_file.exists()) 341 if (!output_file.mkdirs()) 342 throw new IOException("Invalid output file"); 343 if (output_file.exists()) 344 if (!output_file.isDirectory()) 345 throw new IOException("Invalid output file"); 346 } 347 return this.output_file; 348 } 349 350 @Override 351 public String getInputFileString() { 352 String inputFiles = ""; 353 try { 354 for (final File f : this.getInputFiles()) { 355 inputFiles += f.getAbsolutePath() + " "; 356 } 357 } catch (final IOException e) { 358 } 359 inputFiles = inputFiles.trim(); 360 return inputFiles; 361 } 362 363 @Override 364 public String getOutputFileString() { 365 try { 366 return this.getOutputFile().getAbsolutePath(); 367 } catch (final IOException e) { 368 return null; 369 } 370 } 371 372 @Override 373 public String getOtherInfoFile() { 374 return this.otherInfoFile; 375 } 376 377 @Override 378 public ClusterTypeOp getOtherInfoType() { 379 return this.otherClusterType; 380 } 381 382 @Override 383 public Class<? extends SpatialClusters<?>> getClusterClass() { 384 return this.clusterClass; 385 } 386 387 @Override 388 public Class<? extends SpatialClusters<?>> getOtherInfoClass() { 389 return this.otherClusterClass; 390 } 391 392 /** 393 * Set the input files 394 * 395 * @param files 396 */ 397 public void setInputFiles(List<File> files) { 398 this.inputFiles = files; 399 } 400 401 /** 402 * Set the cluster type 403 * 404 * @param clusterType 405 */ 406 public void setClusterType(ClusterType clusterType) { 407 this.clusterType = clusterType; 408 this.clusterTypeOp = (ClusterTypeOp) clusterType.getOptions(); 409 } 410 411 /** 412 * Set the clusterTypeOp 413 * 414 * @param clusterTypeOp 415 */ 416 public void setClusterTypeOp(ClusterTypeOp clusterTypeOp) { 417 this.clusterTypeOp = clusterTypeOp; 418 } 419 420 /** 421 * @return set the root directory 422 * @throws IOException 423 */ 424 public String getInputFileCommonRoot() throws IOException { 425 char[] shortestString = null; 426 int currentLongest = 0; 427 for (final File input : this.getInputFiles()) { 428 final char[] current = input.getAbsolutePath().toCharArray(); 429 if (shortestString == null) { 430 shortestString = current; 431 currentLongest = shortestString.length; 432 continue; 433 } 434 int i = 0; 435 for (; i < currentLongest; i++) { 436 if (shortestString[i] != current[i]) 437 break; 438 } 439 currentLongest = i; 440 } 441 442 final String substring = new String(shortestString).substring(0, currentLongest); 443 final File ret = new File(substring); 444 if (ret.isDirectory() || substring.endsWith("/")) 445 return substring; 446 else 447 return ret.getParent(); 448 } 449 450 /** 451 * @return true if timing info should be printed 452 */ 453 public boolean printTiming() { 454 return this.printTime; 455 } 456 457 /** 458 * Set the number of samples 459 * 460 * @param nsamples 461 * the number of samples 462 */ 463 public void setSamples(int nsamples) { 464 this.samples = nsamples; 465 } 466}