Browse Source

Added mathematics library for transformation support. Implemented PHash and added PHash specific tests

master
Drew Short 11 years ago
parent
commit
8c9936a45d
  1. 5
      pom.xml
  2. 2
      src/main/resources/default.properties
  3. 57
      src/main/scala/com/sothr/imagetools/hash/PHash.scala
  4. 2
      src/test/resources/default.properties
  5. 154
      src/test/scala/com/sothr/imagetools/hash/HashServiceTest.scala

5
pom.xml

@ -80,6 +80,11 @@
<artifactId>thumbnailator</artifactId> <artifactId>thumbnailator</artifactId>
<version>[0.4, 0.5)</version> <version>[0.4, 0.5)</version>
</dependency> </dependency>
<dependency>
<groupId>net.sourceforge.jtransforms</groupId>
<artifactId>jtransforms</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

2
src/main/resources/default.properties

@ -24,7 +24,7 @@ image.dhash.tolerence=8
#set to false if hashing images is taking too long #set to false if hashing images is taking too long
image.phash.use=true image.phash.use=true
image.phash.weight=1.0 image.phash.weight=1.0
image.phash.precision=16
image.phash.precision=32
image.phash.tolerence=8 image.phash.tolerence=8
#Default Thumbnail Settings #Default Thumbnail Settings

57
src/main/scala/com/sothr/imagetools/hash/PHash.scala

@ -1,10 +1,63 @@
package com.sothr.imagetools.hash package com.sothr.imagetools.hash
import edu.emory.mathcs.jtransforms.dct.FloatDCT_2D
import grizzled.slf4j.Logging
/** /**
* Created by dev on 1/22/14. * Created by dev on 1/22/14.
*/ */
object PHash extends PerceptualHasher {
object PHash extends PerceptualHasher with Logging {
def getHash(imageData: Array[Array[Int]]): Long = { def getHash(imageData: Array[Array[Int]]): Long = {
return 0L
//convert the imageData into a FloatArray
val width = imageData.length
val height = imageData(0).length
val imageDataFloat:Array[Array[Float]] = Array.ofDim[Float](height, width)
for (row <- 0 until height) {
for (col <- 0 until width) {
imageDataFloat(row)(col) = imageData(row)(col).toFloat
}
}
//perform transform on the data
val dct:FloatDCT_2D = new FloatDCT_2D(height,width)
dct.forward(imageDataFloat, true)
//extract the DCT data
val dctDataWidth:Int = width / 4
val dctDataHeight:Int = height / 4
//calculate the mean
var total = 0.0f
for (row <- 0 until dctDataHeight) {
for (col <- 0 until dctDataWidth) {
total += imageDataFloat(row)(col)
}
}
val mean = total / (dctDataHeight * dctDataWidth)
//calculate the hash
var hash = 0L
for (row <- 0 until dctDataHeight by 2) {
//process each column
for (col <- 0 until dctDataWidth by 1) {
hash <<= 1
val pixel = imageDataFloat(row)(col)
//If the current pixel is at or above the mean, store it as a one, else store it as a zero
if (pixel >= mean) hash |= 1 else hash |= 0
}
if ((row +1) < dctDataWidth) {
val nextRow = row + 1
//process each column
for (col <- (dctDataWidth - 1) to 0 by -1) {
hash <<= 1
val pixel = imageDataFloat(nextRow)(col)
if (pixel >= mean) hash |= 1 else hash |= 0
}
}
}
debug(s"Computed PHash: $hash from ${dctDataWidth * dctDataHeight} pixels")
hash
} }
} }

2
src/test/resources/default.properties

@ -24,7 +24,7 @@ image.dhash.tolerence=8
#set to false if hashing images is taking too long #set to false if hashing images is taking too long
image.phash.use=true image.phash.use=true
image.phash.weight=1.0 image.phash.weight=1.0
image.phash.precision=16
image.phash.precision=32
image.phash.tolerence=8 image.phash.tolerence=8
#Default Thumbnail Settings #Default Thumbnail Settings

154
src/test/scala/com/sothr/imagetools/hash/HashServiceTest.scala

@ -101,4 +101,158 @@ class HashServiceTest extends BaseTest {
assert(HashService.areDhashSimilar(mediumHash,smallHash)) assert(HashService.areDhashSimilar(mediumHash,smallHash))
} }
def ahashTestCase(filePath:String):Long = {
val sample = new File(filePath)
val image = ImageIO.read(sample)
HashService.getAhash(image)
}
test("Benchmark AHash") {
info("Benchmarking AHash")
info("AHash Large Image 3684x2736")
val largeTime1 = getTime { ahashTestCase(TestParams.LargeSampleImage1) }
val largeTime2 = getTime { ahashTestCase(TestParams.LargeSampleImage1) }
val largeTime3 = getTime { ahashTestCase(TestParams.LargeSampleImage1) }
val largeTime4 = getTime { ahashTestCase(TestParams.LargeSampleImage1) }
val largeTime5 = getTime { ahashTestCase(TestParams.LargeSampleImage1) }
val largeMean = getMean(largeTime1, largeTime2, largeTime3, largeTime4, largeTime5)
info(s"The mean time of 5 tests for large was: $largeMean ms")
info("AHash Medium Image 1824x1368")
val mediumTime1 = getTime { ahashTestCase(TestParams.MediumSampleImage1) }
val mediumTime2 = getTime { ahashTestCase(TestParams.MediumSampleImage1) }
val mediumTime3 = getTime { ahashTestCase(TestParams.MediumSampleImage1) }
val mediumTime4 = getTime { ahashTestCase(TestParams.MediumSampleImage1) }
val mediumTime5 = getTime { ahashTestCase(TestParams.MediumSampleImage1) }
val mediumMean = getMean(mediumTime1, mediumTime2, mediumTime3, mediumTime4, mediumTime5)
info(s"The mean time of 5 tests for medium was: $mediumMean ms")
info("AHash Small Image 912x684")
val smallTime1 = getTime { ahashTestCase(TestParams.SmallSampleImage1) }
val smallTime2 = getTime { ahashTestCase(TestParams.SmallSampleImage1) }
val smallTime3 = getTime { ahashTestCase(TestParams.SmallSampleImage1) }
val smallTime4 = getTime { ahashTestCase(TestParams.SmallSampleImage1) }
val smallTime5 = getTime { ahashTestCase(TestParams.SmallSampleImage1) }
val smallMean = getMean(smallTime1, smallTime2, smallTime3, smallTime4, smallTime5)
info(s"The mean time of 5 tests for small was: $smallMean ms")
assert(true)
}
test("Calculate AHash Large Sample Image 1") {
debug("Starting 'Calculate AHash Large Sample Image 1' test")
val sample = new File(TestParams.LargeSampleImage1)
debug(s"Testing File: ${sample.getAbsolutePath} exists: ${sample.exists}")
val image = ImageIO.read(sample)
debug(s"Image: width: ${image.getWidth} height: ${image.getHeight}")
val hash = HashService.getAhash(image)
debug(s"Testing that $hash = 36070299219713907L")
assert(hash == 36070299219713907L)
}
test("Calculate AHash Medium Sample Image 1") {
debug("Starting 'Calculate AHash Medium Sample Image 1' test")
val sample = new File(TestParams.MediumSampleImage1)
debug(s"Testing File: ${sample.getAbsolutePath} exists: ${sample.exists}")
val image = ImageIO.read(sample)
debug(s"Image: width: ${image.getWidth} height: ${image.getHeight}")
val hash = HashService.getAhash(image)
debug(s"Testing that $hash = 36070299219713907L")
assert(hash == 36070299219713907L)
}
test("Calculate AHash Small Sample Image 1") {
debug("Starting 'Calculate AHash Small Sample Image 1' test")
val sample = new File(TestParams.SmallSampleImage1)
debug(s"Testing File: ${sample.getAbsolutePath} exists: ${sample.exists}")
val image = ImageIO.read(sample)
debug(s"Image: width: ${image.getWidth} height: ${image.getHeight}")
val hash = HashService.getAhash(image)
debug(s"Testing that $hash = 36070299219713907L")
assert(hash == 36070299219713907L)
}
test("AHash Of Large, Medium, And Small Sample 1 Must Be Similar") {
val largeHash = ahashTestCase(TestParams.LargeSampleImage1)
val mediumHash = ahashTestCase(TestParams.MediumSampleImage1)
val smallHash = ahashTestCase(TestParams.SmallSampleImage1)
assert(HashService.areAhashSimilar(largeHash,mediumHash))
assert(HashService.areAhashSimilar(largeHash,smallHash))
assert(HashService.areAhashSimilar(mediumHash,smallHash))
}
def phashTestCase(filePath:String):Long = {
val sample = new File(filePath)
val image = ImageIO.read(sample)
HashService.getPhash(image)
}
test("Benchmark PHash") {
info("Benchmarking PHash")
info("PHash Large Image 3684x2736")
val largeTime1 = getTime { phashTestCase(TestParams.LargeSampleImage1) }
val largeTime2 = getTime { phashTestCase(TestParams.LargeSampleImage1) }
val largeTime3 = getTime { phashTestCase(TestParams.LargeSampleImage1) }
val largeTime4 = getTime { phashTestCase(TestParams.LargeSampleImage1) }
val largeTime5 = getTime { phashTestCase(TestParams.LargeSampleImage1) }
val largeMean = getMean(largeTime1, largeTime2, largeTime3, largeTime4, largeTime5)
info(s"The mean time of 5 tests for large was: $largeMean ms")
info("PHash Medium Image 1824x1368")
val mediumTime1 = getTime { phashTestCase(TestParams.MediumSampleImage1) }
val mediumTime2 = getTime { phashTestCase(TestParams.MediumSampleImage1) }
val mediumTime3 = getTime { phashTestCase(TestParams.MediumSampleImage1) }
val mediumTime4 = getTime { phashTestCase(TestParams.MediumSampleImage1) }
val mediumTime5 = getTime { phashTestCase(TestParams.MediumSampleImage1) }
val mediumMean = getMean(mediumTime1, mediumTime2, mediumTime3, mediumTime4, mediumTime5)
info(s"The mean time of 5 tests for medium was: $mediumMean ms")
info("PHash Small Image 912x684")
val smallTime1 = getTime { phashTestCase(TestParams.SmallSampleImage1) }
val smallTime2 = getTime { phashTestCase(TestParams.SmallSampleImage1) }
val smallTime3 = getTime { phashTestCase(TestParams.SmallSampleImage1) }
val smallTime4 = getTime { phashTestCase(TestParams.SmallSampleImage1) }
val smallTime5 = getTime { phashTestCase(TestParams.SmallSampleImage1) }
val smallMean = getMean(smallTime1, smallTime2, smallTime3, smallTime4, smallTime5)
info(s"The mean time of 5 tests for small was: $smallMean ms")
assert(true)
}
test("Calculate PHash Large Sample Image 1") {
debug("Starting 'Calculate PHash Large Sample Image 1' test")
val sample = new File(TestParams.LargeSampleImage1)
debug(s"Testing File: ${sample.getAbsolutePath} exists: ${sample.exists}")
val image = ImageIO.read(sample)
debug(s"Image: width: ${image.getWidth} height: ${image.getHeight}")
val hash = HashService.getPhash(image)
debug(s"Testing that $hash = -9154589787976242949L")
assert(hash == -9154589787976242949L)
}
test("Calculate PHash Medium Sample Image 1") {
debug("Starting 'Calculate PHash Medium Sample Image 1' test")
val sample = new File(TestParams.MediumSampleImage1)
debug(s"Testing File: ${sample.getAbsolutePath} exists: ${sample.exists}")
val image = ImageIO.read(sample)
debug(s"Image: width: ${image.getWidth} height: ${image.getHeight}")
val hash = HashService.getPhash(image)
debug(s"Testing that $hash = -9154589787976242949L")
assert(hash == -9154589787976242949L)
}
test("Calculate PHash Small Sample Image 1") {
debug("Starting 'Calculate PHash Small Sample Image 1' test")
val sample = new File(TestParams.SmallSampleImage1)
debug(s"Testing File: ${sample.getAbsolutePath} exists: ${sample.exists}")
val image = ImageIO.read(sample)
debug(s"Image: width: ${image.getWidth} height: ${image.getHeight}")
val hash = HashService.getPhash(image)
debug(s"Testing that $hash = -9154589787976242949L")
assert(hash == -9154589787976242949L)
}
test("PHash Of Large, Medium, And Small Sample 1 Must Be Similar") {
val largeHash = phashTestCase(TestParams.LargeSampleImage1)
val mediumHash = phashTestCase(TestParams.MediumSampleImage1)
val smallHash = phashTestCase(TestParams.SmallSampleImage1)
assert(HashService.arePhashSimilar(largeHash,mediumHash))
assert(HashService.arePhashSimilar(largeHash,smallHash))
assert(HashService.arePhashSimilar(mediumHash,smallHash))
}
} }
Loading…
Cancel
Save