/src/ExifMiner.groovy

https://github.com/frederic-schmaljohann/GroovyExifMiner · Groovy · 127 lines · 72 code · 15 blank · 40 comment · 8 complexity · cfe5823d45e18927b1d7fdc0c90993ba MD5 · raw file

  1. import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics
  2. import com.drew.imaging.ImageMetadataReader
  3. import com.drew.metadata.Metadata
  4. import com.drew.metadata.exif.ExifIFD0Directory;
  5. import com.drew.metadata.exif.ExifSubIFDDirectory
  6. /**
  7. * Prints a bunch of stats for Exif related data
  8. * using metadata-extractor
  9. *
  10. * @author Frederic Schmaljohann
  11. */
  12. class ExifAnalyzer {
  13. /**
  14. * File extension of image files
  15. */
  16. static final String REGEX_PICTURE_FILE = /.*jpg|.*cr2/
  17. /**
  18. * Directory to be traversed (recursively)
  19. */
  20. static final String IMAGE_DIR = 'SOME_DIR'
  21. static final String SEPARATOR = '==='
  22. DescriptiveStatistics focalLengths = new DescriptiveStatistics()
  23. DescriptiveStatistics isos = new DescriptiveStatistics()
  24. DescriptiveStatistics apertures = new DescriptiveStatistics()
  25. /**
  26. * Computes descriptive stats for focal length, iso and aperture values
  27. * @return
  28. */
  29. def computeStats() {
  30. new File(IMAGE_DIR).traverse {
  31. if (it.isFile() && it.canonicalPath.toLowerCase() ==~ REGEX_PICTURE_FILE) {
  32. try {
  33. extractData(it.canonicalPath)
  34. }
  35. catch (e) {
  36. println e.message
  37. }
  38. }
  39. }
  40. }
  41. private printStats() {
  42. println SEPARATOR + 'Focal length' + SEPARATOR
  43. printStats(focalLengths)
  44. println '\n' + SEPARATOR + 'ISO' + SEPARATOR
  45. printStats(isos)
  46. println '\n' + SEPARATOR + 'Apertures' + SEPARATOR
  47. printStats(apertures)
  48. }
  49. /**
  50. * Extract the data and put it into statistics
  51. * @param pictureFile
  52. * @return
  53. */
  54. private extractData(String pictureFile) {
  55. Metadata meta = ImageMetadataReader.readMetadata(new File(pictureFile))
  56. ExifSubIFDDirectory dir = meta.getDirectory(ExifSubIFDDirectory.class)
  57. ExifIFD0Directory dir2 = meta.getDirectory(ExifIFD0Directory.class)
  58. focalLengths.addValue(Double.parseDouble(dir.getString(ExifSubIFDDirectory.TAG_FOCAL_LENGTH)))
  59. isos.addValue(Double.parseDouble(dir.getString(ExifSubIFDDirectory.TAG_ISO_EQUIVALENT)))
  60. double aperture = computeAperture(dir.getString(ExifSubIFDDirectory.TAG_APERTURE))
  61. apertures.addValue(aperture)
  62. }
  63. /**
  64. * Compute aperture
  65. * @param apertureString
  66. * @return
  67. */
  68. def computeAperture(String apertureString) {
  69. Math.round(Math.pow(2, (parseFraction() / 2)) * 10) / 10
  70. }
  71. /**
  72. * Print statistics
  73. * @param stats
  74. * @return
  75. */
  76. def printStats(DescriptiveStatistics stats) {
  77. println stats
  78. printFrequencies(stats.sortedValues)
  79. }
  80. /**
  81. * parses a double or fraction, e.g. 127/56 or 12.4
  82. * @param stringToParse
  83. * @return
  84. */
  85. def double parseFraction(String stringToParse) {
  86. if (stringToParse.indexOf('/') == -1) {
  87. return Double.parseDouble(stringToParse)
  88. }
  89. double d = Double.parseDouble(stringToParse.substring(0, stringToParse.indexOf('/')))
  90. d /= Double.parseDouble(stringToParse.substring(stringToParse.indexOf('/')+1))
  91. }
  92. /**
  93. * Print the frequencies
  94. * @param sortedArray
  95. */
  96. def printFrequencies(double[] sortedArray) {
  97. double currValue = sortedArray[0]
  98. int currCounter = 1
  99. for (int i=0;i < sortedArray.size(); i++) {
  100. if (currValue == sortedArray[i]) {
  101. currCounter++
  102. }
  103. else {
  104. println currValue + '->' + currCounter
  105. currValue = sortedArray[i]
  106. }
  107. }
  108. }
  109. }
  110. ExifAnalyzer ee = new ExifAnalyzer()
  111. ee.computeStats()
  112. ee.printStats()