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.BufferedInputStream; 033import java.io.BufferedReader; 034import java.io.DataInputStream; 035import java.io.File; 036import java.io.FileInputStream; 037import java.io.IOException; 038import java.io.InputStream; 039import java.io.InputStreamReader; 040import java.util.Arrays; 041import java.util.Scanner; 042 043import org.openimaj.feature.local.list.LocalFeatureList; 044import org.openimaj.image.feature.local.affine.AffineSimulationKeypoint; 045import org.openimaj.image.feature.local.keypoints.Keypoint; 046import org.openimaj.io.IOUtils; 047 048/** 049 * Different file formats containing local features. 050 * 051 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 052 * 053 */ 054public enum FileType { 055 /** 056 * Auto-guess between Lowe's ASCII keypoints format or the OpenIMAJ binary 057 * format. 058 * 059 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 060 * 061 */ 062 LOWE_KEYPOINT { 063 @Override 064 public Header readHeader(File file) throws IOException { 065 try { 066 return BINARY_KEYPOINT.readHeader(file); 067 } catch (final Exception e) { 068 return LOWE_KEYPOINT_ASCII.readHeader(file); 069 } 070 } 071 072 @Override 073 public Header readHeader(InputStream bis) throws IOException { 074 final BufferedInputStream bstream = new BufferedInputStream(bis); 075 076 final boolean binary = IOUtils.isBinary(bstream, LocalFeatureList.BINARY_HEADER); 077 078 if (binary) 079 return BINARY_KEYPOINT.readHeader(bstream); 080 else 081 return LOWE_KEYPOINT_ASCII.readHeader(bstream); 082 } 083 084 @Override 085 public FeatureFile read(File file) throws IOException { 086 try { 087 return BINARY_KEYPOINT.read(file); 088 } catch (final Exception e) { 089 return LOWE_KEYPOINT_ASCII.read(file); 090 } 091 } 092 093 @Override 094 public FeatureFile read(InputStream stream) throws IOException { 095 final BufferedInputStream bstream = new BufferedInputStream(stream); 096 097 final boolean binary = IOUtils.isBinary(bstream, LocalFeatureList.BINARY_HEADER); 098 099 if (binary) 100 return BINARY_KEYPOINT.read(bstream); 101 else 102 return LOWE_KEYPOINT_ASCII.read(bstream); 103 } 104 105 @Override 106 public byte[][] readFeatures(File file, int... index) throws IOException { 107 try { 108 return BINARY_KEYPOINT.readFeatures(file, index); 109 } catch (final Exception e) { 110 return LOWE_KEYPOINT_ASCII.readFeatures(file, index); 111 } 112 } 113 114 @Override 115 public byte[][] readFeatures(InputStream file, int... index) throws IOException { 116 try { 117 return BINARY_KEYPOINT.readFeatures(file, index); 118 } catch (final Exception e) { 119 return LOWE_KEYPOINT_ASCII.readFeatures(file, index); 120 } 121 } 122 }, 123 /** 124 * OpenIMAJ binary list of keypoints format 125 * 126 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 127 * 128 */ 129 BINARY_KEYPOINT { 130 @Override 131 public Header readHeader(File file) throws IOException { 132 BufferedInputStream bis = null; 133 134 try { 135 bis = new BufferedInputStream(new FileInputStream(file)); 136 final byte[] header = new byte[LocalFeatureList.BINARY_HEADER.length]; 137 bis.read(header, 0, LocalFeatureList.BINARY_HEADER.length); 138 139 if (!Arrays.equals(header, LocalFeatureList.BINARY_HEADER)) { 140 throw new IOException("File \"" + file + "\"is not a binary keypoint file"); 141 } 142 143 final DataInputStream dis = new DataInputStream(bis); 144 145 final Header h = new Header(); 146 h.nfeatures = dis.readInt(); 147 h.ndims = dis.readInt(); 148 return h; 149 } finally { 150 try { 151 bis.close(); 152 } catch (final IOException e) { 153 } 154 } 155 } 156 157 @Override 158 public Header readHeader(InputStream bis) throws IOException { 159 final byte[] header = new byte[LocalFeatureList.BINARY_HEADER.length]; 160 bis.read(header, 0, LocalFeatureList.BINARY_HEADER.length); 161 162 if (!Arrays.equals(header, LocalFeatureList.BINARY_HEADER)) { 163 throw new IOException("Stream does not contain a binary keypoint"); 164 } 165 166 final DataInputStream dis = new DataInputStream(bis); 167 168 final Header h = new Header(); 169 h.nfeatures = dis.readInt(); 170 h.ndims = dis.readInt(); 171 return h; 172 } 173 174 @Override 175 public FeatureFile read(File file) throws IOException { 176 final FeatureFile ff = new StreamedFeatureFile(file); 177 return ff; 178 } 179 180 @Override 181 public FeatureFile read(InputStream stream) throws IOException { 182 final FeatureFile ff = new StreamedFeatureFile(stream); 183 return ff; 184 } 185 186 @Override 187 public byte[][] readFeatures(File file, int... index) throws IOException { 188 return readFeatures(new FileInputStream(file), index); 189 } 190 191 @Override 192 public byte[][] readFeatures(InputStream file, int... index) throws IOException { 193 BufferedInputStream bis = null; 194 final byte[][] data = new byte[index.length][]; 195 try { 196 bis = new BufferedInputStream(file); 197 final byte[] header = new byte[LocalFeatureList.BINARY_HEADER.length]; 198 bis.read(header, 0, LocalFeatureList.BINARY_HEADER.length); 199 200 if (!Arrays.equals(header, LocalFeatureList.BINARY_HEADER)) { 201 throw new IOException("File \"" + file + "\"is not a binary keypoint file"); 202 } 203 204 final DataInputStream dis = new DataInputStream(bis); 205 206 final Header h = new Header(); 207 h.nfeatures = dis.readInt(); 208 h.ndims = dis.readInt(); 209 210 // == float * 4 + int * KeypointEngine.VecLength 211 final int vecLength = (16 + h.ndims); 212 int skipped = 0; 213 Arrays.sort(index); 214 for (int i = 0; i < index.length; i++) { 215 int toSkip = (index[i] * vecLength) - skipped; 216 skipped += toSkip; 217 while (toSkip > 0) 218 toSkip -= dis.skip(toSkip); 219 220 final Keypoint kp = new Keypoint(); 221 kp.x = dis.readFloat(); 222 kp.y = dis.readFloat(); 223 kp.scale = dis.readFloat(); 224 kp.ori = dis.readFloat(); 225 kp.ivec = new byte[h.ndims]; 226 dis.read(kp.ivec, 0, h.ndims); 227 data[i] = kp.ivec; 228 skipped += vecLength; 229 } 230 231 } finally { 232 try { 233 bis.close(); 234 } catch (final IOException e) { 235 } 236 } 237 return data; 238 } 239 }, 240 /** 241 * Format defined by Lowe's "keypoints" binary 242 * 243 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 244 */ 245 LOWE_KEYPOINT_ASCII { 246 @Override 247 public byte[][] readFeatures(File file, int... index) throws IOException { 248 return AsciiInterestPoint.readData(file, index, false, AsciiInterestPoint.NUM_CIRCLE_LOC_FEATS); 249 } 250 251 @Override 252 public Header readHeader(File file) throws IOException { 253 return AsciiInterestPoint.readHeader(file, false); 254 } 255 256 @Override 257 public Header readHeader(InputStream stream) throws IOException { 258 return AsciiInterestPoint.readHeader(new Scanner(stream), false); 259 } 260 261 @Override 262 public byte[][] readFeatures(File file) throws IOException { 263 return AsciiInterestPoint.readData(file, false, AsciiInterestPoint.NUM_CIRCLE_LOC_FEATS); 264 } 265 266 @Override 267 public FeatureFile read(File file) throws IOException { 268 return AsciiInterestPoint.read(file, false, AsciiInterestPoint.NUM_CIRCLE_LOC_FEATS); 269 } 270 271 @Override 272 public FeatureFile read(InputStream source) throws IOException { 273 return AsciiInterestPoint.read(source, false, AsciiInterestPoint.NUM_CIRCLE_LOC_FEATS); 274 } 275 }, 276 /** 277 * Ellipse format used by Oxford tools 278 * 279 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 280 */ 281 ELLIPSE_ASCII { 282 @Override 283 public byte[][] readFeatures(File file, int... index) throws IOException { 284 return AsciiInterestPoint.readData(file, index, true, AsciiInterestPoint.NUM_ELLIPSE_LOC_FEATS); 285 } 286 287 @Override 288 public Header readHeader(File file) throws IOException { 289 return AsciiInterestPoint.readHeader(file, true); 290 } 291 292 @Override 293 public Header readHeader(InputStream stream) throws IOException { 294 return AsciiInterestPoint.readHeader(new Scanner(stream), true); 295 } 296 297 @Override 298 public byte[][] readFeatures(File file) throws IOException { 299 return AsciiInterestPoint.readData(file, true, AsciiInterestPoint.NUM_ELLIPSE_LOC_FEATS); 300 } 301 302 @Override 303 public FeatureFile read(File file) throws IOException { 304 return AsciiInterestPoint.read(file, true, AsciiInterestPoint.NUM_ELLIPSE_LOC_FEATS); 305 } 306 307 @Override 308 public FeatureFile read(InputStream source) throws IOException { 309 return AsciiInterestPoint.read(source, true, AsciiInterestPoint.NUM_ELLIPSE_LOC_FEATS); 310 } 311 }, 312 /** 313 * KOEN1 ascii format used by Koen van der Sande's colour sift tools. 314 * 315 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 316 * 317 */ 318 KOEN1_ASCII { 319 @Override 320 public FeatureFile read(InputStream file) throws IOException { 321 // create a BufferedReader for the file 322 final BufferedReader input = new BufferedReader(new InputStreamReader(file)); 323 324 // read the first line and check that it starts with KOEN1 325 // this way there is no need to worry about newline characters 326 // in the end of string, if we used .equals 327 if (!(input.readLine().startsWith("KOEN1"))) { 328 throw new IOException( 329 "The specified file is not a KOEN1 type file"); 330 } else { 331 // read the next two lines and Integer.parseInt(); to get ndims 332 // & nfeatures 333 final int ndims = Integer.parseInt(input.readLine()); 334 final int nfeatures = Integer.parseInt(input.readLine()); 335 336 final byte[][] data = new byte[nfeatures][ndims]; 337 final String[] locations = new String[nfeatures]; 338 339 if (nfeatures == 0) { 340 final FeatureFile ff = new MemoryFeatureFile(new byte[0][], new String[0]); 341 return ff; 342 } 343 344 for (int i = 0; i < nfeatures; i++) { 345 346 // read the next line and split on ';' 347 final String[] parts = input.readLine().split(";"); 348 349 // put first element (substring) of the split into 350 // FeatureFile.locationInfo 351 locations[i] = parts[0]; 352 353 // split second element (substring) on ' ' (a space) 354 final String[] fvector = parts[1].trim().split(" "); 355 // parse each element as int and put into array 356 for (int j = 0; j < ndims; j++) { 357 // store array in FeatureFiel.data 358 data[i][j] = (byte) (Integer.parseInt(fvector[j]) - 128); 359 } 360 361 } 362 final FeatureFile ff = new MemoryFeatureFile(data, locations); 363 // return FeatureFile 364 return ff; 365 } 366 } 367 368 @Override 369 public FeatureFile read(File source) throws IOException { 370 return read(new FileInputStream(source)); 371 } 372 }, 373 /** 374 * OpenIMAJ ASIFTENRICHED format 375 * 376 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 377 * 378 */ 379 ASIFTENRICHED { 380 @Override 381 public Header readHeader(File file) throws IOException { 382 383 try { 384 return ASIFTENRICHED_BINARY.readHeader(file); 385 } 386 catch (final Exception e) { 387 return ASIFTENRICHED_ASCII.readHeader(file); 388 } 389 } 390 391 @Override 392 public Header readHeader(InputStream bis) throws IOException { 393 final BufferedInputStream bstream = new BufferedInputStream(bis); 394 395 final boolean binary = IOUtils.isBinary(bstream, LocalFeatureList.BINARY_HEADER); 396 397 if (binary) 398 return ASIFTENRICHED_BINARY.readHeader(bstream); 399 else 400 return ASIFTENRICHED_ASCII.readHeader(bstream); 401 402 } 403 404 @Override 405 public FeatureFile read(File file) throws IOException { 406 try { 407 return ASIFTENRICHED_BINARY.read(file); 408 } 409 catch (final Exception e) { 410 return ASIFTENRICHED_ASCII.read(file); 411 } 412 } 413 414 @Override 415 public FeatureFile read(InputStream stream) throws IOException { 416 final BufferedInputStream bstream = new BufferedInputStream(stream); 417 418 final boolean binary = IOUtils.isBinary(bstream, LocalFeatureList.BINARY_HEADER); 419 420 if (binary) 421 return ASIFTENRICHED_BINARY.read(bstream); 422 else 423 return ASIFTENRICHED_ASCII.read(bstream); 424 } 425 426 @Override 427 public byte[][] readFeatures(File file, int... index) throws IOException { 428 try { 429 return ASIFTENRICHED_BINARY.readFeatures(file, index); 430 } 431 catch (final Exception e) { 432 return ASIFTENRICHED_ASCII.readFeatures(file, index); 433 } 434 } 435 436 @Override 437 public byte[][] readFeatures(InputStream file, int... index) throws IOException { 438 try { 439 return ASIFTENRICHED_BINARY.readFeatures(file, index); 440 } 441 catch (final Exception e) { 442 return ASIFTENRICHED_ASCII.readFeatures(file, index); 443 } 444 } 445 }, 446 /** 447 * OpenIMAJ ASIFTENRICHED binary format 448 * 449 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 450 */ 451 ASIFTENRICHED_BINARY { 452 @Override 453 public Header readHeader(File file) throws IOException { 454 BufferedInputStream bis = null; 455 456 try { 457 bis = new BufferedInputStream(new FileInputStream(file)); 458 final byte[] header = new byte[LocalFeatureList.BINARY_HEADER.length]; 459 bis.read(header, 0, LocalFeatureList.BINARY_HEADER.length); 460 461 if (!Arrays.equals(header, LocalFeatureList.BINARY_HEADER)) { 462 throw new IOException("File \"" + file + "\"is not a binary keypoint file"); 463 } 464 465 final DataInputStream dis = new DataInputStream(bis); 466 467 final Header h = new Header(); 468 h.nfeatures = dis.readInt(); 469 h.ndims = dis.readInt(); 470 return h; 471 } finally { 472 try { 473 bis.close(); 474 } catch (final IOException e) { 475 } 476 } 477 } 478 479 @Override 480 public Header readHeader(InputStream bis) throws IOException { 481 final byte[] header = new byte[LocalFeatureList.BINARY_HEADER.length]; 482 bis.read(header, 0, LocalFeatureList.BINARY_HEADER.length); 483 484 if (!Arrays.equals(header, LocalFeatureList.BINARY_HEADER)) { 485 throw new IOException("Strean dies not contain a binary keypoint"); 486 } 487 488 final DataInputStream dis = new DataInputStream(bis); 489 490 final Header h = new Header(); 491 h.nfeatures = dis.readInt(); 492 h.ndims = dis.readInt(); 493 return h; 494 } 495 496 @Override 497 public FeatureFile read(File file) throws IOException { 498 final StreamedFeatureFile ff = new StreamedFeatureFile(file, AffineSimulationKeypoint.class); 499 ff.setIteratorType(AffineSimulationKeypointListArrayIterator.class); 500 return ff; 501 } 502 503 @Override 504 public FeatureFile read(InputStream stream) throws IOException { 505 final StreamedFeatureFile ff = new StreamedFeatureFile(stream, AffineSimulationKeypoint.class); 506 ff.setIteratorType(AffineSimulationKeypointListArrayIterator.class); 507 return ff; 508 } 509 510 @Override 511 public byte[][] readFeatures(File file, int... index) throws IOException { 512 return readFeatures(new FileInputStream(file), index); 513 } 514 515 @Override 516 public byte[][] readFeatures(InputStream file, int... index) throws IOException { 517 BufferedInputStream bis = null; 518 final byte[][] data = new byte[index.length][]; 519 try { 520 bis = new BufferedInputStream(file); 521 final byte[] header = new byte[LocalFeatureList.BINARY_HEADER.length]; 522 bis.read(header, 0, LocalFeatureList.BINARY_HEADER.length); 523 524 if (!Arrays.equals(header, LocalFeatureList.BINARY_HEADER)) { 525 throw new IOException("File \"" + file + "\"is not a binary keypoint file"); 526 } 527 528 final DataInputStream dis = new DataInputStream(bis); 529 530 final Header h = new Header(); 531 h.nfeatures = dis.readInt(); 532 h.ndims = dis.readInt(); 533 534 // == float * 6 + int + KeypointEngine.VecLength 535 final int vecLength = (28 + h.ndims); 536 int skipped = 0; 537 Arrays.sort(index); 538 for (int i = 0; i < index.length; i++) { 539 int toSkip = (index[i] * vecLength) - skipped; 540 skipped += toSkip; 541 while (toSkip > 0) 542 toSkip -= dis.skip(toSkip); 543 544 final AffineSimulationKeypoint kp = new AffineSimulationKeypoint(h.ndims); 545 kp.readBinary(dis); 546 data[i] = kp.ivec; 547 skipped += vecLength; 548 } 549 550 } finally { 551 try { 552 bis.close(); 553 } catch (final IOException e) { 554 } 555 } 556 return data; 557 } 558 }, 559 /** 560 * OpenIMAJ ASIFTENRICHED ascii format 561 * 562 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 563 */ 564 ASIFTENRICHED_ASCII { 565 @Override 566 public byte[][] readFeatures(File file, int... index) throws IOException { 567 return AsciiInterestPoint.readData(file, index, false, AsciiInterestPoint.NUM_ASIFT_LOC_FEATS); 568 } 569 570 @Override 571 public Header readHeader(File file) throws IOException { 572 return AsciiInterestPoint.readHeader(file, false); 573 } 574 575 @Override 576 public Header readHeader(InputStream stream) throws IOException { 577 return AsciiInterestPoint.readHeader(new Scanner(stream), false); 578 } 579 580 @Override 581 public byte[][] readFeatures(File file) throws IOException { 582 return AsciiInterestPoint.readData(file, false, AsciiInterestPoint.NUM_ASIFT_LOC_FEATS); 583 } 584 585 @Override 586 public FeatureFile read(File file) throws IOException { 587 return AsciiInterestPoint.read(file, false, AsciiInterestPoint.NUM_ASIFT_LOC_FEATS); 588 } 589 590 @Override 591 public FeatureFile read(InputStream source) throws IOException { 592 return AsciiInterestPoint.read(source, false, AsciiInterestPoint.NUM_ASIFT_LOC_FEATS); 593 } 594 }; 595 596 /** 597 * Read the header (num features and dimensionality of features) from given 598 * file. Override for performance. 599 * 600 * @param file 601 * @return header 602 * @throws IOException 603 */ 604 public Header readHeader(File file) throws IOException { 605 final Header header = new Header(); 606 607 final FeatureFile ff = read(file); 608 if (ff.size() > 0) { 609 header.nfeatures = ff.size(); 610 header.ndims = ff.get(0).data.length; 611 } else { 612 header.nfeatures = 0; 613 header.ndims = 0; 614 } 615 616 return header; 617 } 618 619 /** 620 * Read the header (num features and dimensionality of features) from given 621 * file. Override for performance. 622 * 623 * @param stream 624 * @return header 625 * @throws IOException 626 */ 627 public Header readHeader(InputStream stream) throws IOException { 628 final Header header = new Header(); 629 630 final FeatureFile ff = read(stream); 631 if (ff.size() > 0) { 632 header.nfeatures = ff.size(); 633 header.ndims = ff.get(0).data.length; 634 } else { 635 header.nfeatures = 0; 636 header.ndims = 0; 637 } 638 639 return header; 640 } 641 642 /** 643 * Read features at given indices from the file. Override for performance. 644 * 645 * @param file 646 * @param index 647 * @return the feature data 648 * @throws IOException 649 */ 650 public byte[][] readFeatures(File file, int... index) throws IOException { 651 652 return readFeatures(new FileInputStream(file), index); 653 } 654 655 /** 656 * Read features at given indices from an input stream. Override for 657 * performance. 658 * 659 * @param stream 660 * @param index 661 * @return the feature data 662 * @throws IOException 663 */ 664 public byte[][] readFeatures(InputStream stream, int... index) throws IOException { 665 666 final byte[][] features = readFeatures(stream); 667 final byte[][] selected = new byte[index.length][]; 668 for (int i = 0; i < index.length; i++) { 669 selected[i] = features[index[i]]; 670 } 671 return selected; 672 } 673 674 /** 675 * Read all the features from the file. Override for performance. 676 * 677 * @param file 678 * @return the feature data 679 * @throws IOException 680 */ 681 public byte[][] readFeatures(File file) throws IOException { 682 final FeatureFile ff = read(file); 683 final byte[][] data = new byte[ff.size()][]; 684 int i = 0; 685 for (final FeatureFileFeature fff : ff) { 686 data[i++] = fff.data; 687 } 688 return data; 689 } 690 691 /** 692 * Read all the features from the file. Override for performance. 693 * 694 * @param stream 695 * @return the feature data 696 * @throws IOException 697 */ 698 public byte[][] readFeatures(InputStream stream) throws IOException { 699 final FeatureFile ff = read(stream); 700 final byte[][] data = new byte[ff.size()][]; 701 int i = 0; 702 for (final FeatureFileFeature fff : ff) { 703 data[i++] = fff.data; 704 } 705 return data; 706 } 707 708 /** 709 * Read a file 710 * 711 * @param file 712 * @return the features 713 * @throws IOException 714 */ 715 public abstract FeatureFile read(File file) throws IOException; 716 717 /** 718 * Read a file 719 * 720 * @param source 721 * @return the features 722 * @throws IOException 723 */ 724 public abstract FeatureFile read(InputStream source) throws IOException; 725}