diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala b/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
index 7818158..7ad6e0e 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
@@ -7,10 +7,9 @@ import akka.actor.{Actor, ActorLogging, ActorRef, PoisonPill, Props}
import akka.pattern.ask
import akka.routing.{Broadcast, RoundRobinPool, SmallestMailboxPool}
import akka.util.Timeout
-import com.sothr.imagetools.hash.HashService
+import com.sothr.imagetools.hash.{HashService, ImageHash}
import com.sothr.imagetools.engine.image.{Image, ImageService, SimilarImages}
import com.sothr.imagetools.engine.util.{PropertiesService, PropertyEnum}
-import com.sothr.imagetools.hash.dto.ImageHashDTO
import scala.collection.mutable
import scala.concurrent.Await
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala b/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala
index bdaa5f5..7862747 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala
@@ -8,8 +8,7 @@ import com.sothr.imagetools.engine.AppConfig
import com.sothr.imagetools.engine.dao.ImageDAO
import com.sothr.imagetools.engine.util.{PropertiesService, PropertyEnum}
import com.sothr.imagetools.engine.vo.ImageHashVO
-import com.sothr.imagetools.hash.HashService
-import com.sothr.imagetools.hash.dto.ImageHashDTO
+import com.sothr.imagetools.hash.{HashService, ImageHash}
import com.sothr.imagetools.hash.util.ImageUtil
import grizzled.slf4j.Logging
import net.sf.ehcache.Element
@@ -36,8 +35,8 @@ object ImageService extends Logging {
bufferedImage,
file.getAbsolutePath)
- var thumbnailPath = lookupThumbnailPath(hashes.getFileHash)
- if (thumbnailPath == null) thumbnailPath = getThumbnail(bufferedImage, hashes.getFileHash)
+ var thumbnailPath = lookupThumbnailPath(hashes.fileHash)
+ if (thumbnailPath == null) thumbnailPath = getThumbnail(bufferedImage, hashes.fileHash)
val imageSize = {
(bufferedImage.getWidth, bufferedImage.getHeight)
}
@@ -52,11 +51,11 @@ object ImageService extends Logging {
null
}
- def convertToImageHashDTO(imageHashVO: ImageHashVO): ImageHashDTO = {
- new ImageHashDTO(imageHashVO.ahash, imageHashVO.dhash, imageHashVO.phash, imageHashVO.fileHash)
+ def convertToImageHashDTO(imageHashVO: ImageHashVO): ImageHash = {
+ new ImageHash(imageHashVO.ahash, imageHashVO.dhash, imageHashVO.phash, imageHashVO.fileHash)
}
- def convertToImageHashVO(imageHashDTO: ImageHashDTO): ImageHashVO = {
+ def convertToImageHashVO(imageHashDTO: ImageHash): ImageHashVO = {
new ImageHashVO(imageHashDTO.ahash, imageHashDTO.dhash, imageHashDTO.phash, imageHashDTO.fileHash)
}
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala
index 5968342..65cdfa9 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala
@@ -3,7 +3,7 @@ package com.sothr.imagetools.engine.util
import java.io.{File, FileOutputStream, PrintStream}
import java.util.Properties
-import com.sothr.imagetools.hash.dto.HashSettingDTO
+import com.sothr.imagetools.hash.HashSetting
import com.typesafe.config.{Config, ConfigFactory, ConfigRenderOptions}
import grizzled.slf4j.Logging
@@ -13,19 +13,19 @@ import grizzled.slf4j.Logging
object PropertiesService extends Logging {
//OS information
- val OS = System.getProperty("os.name", "UNKNOWN")
- val OS_VERSION = System.getProperty("os.version", "UNKNOWN")
- val OS_ARCH = System.getProperty("os.arch", "UNKNOWN")
+ val OS: String = System.getProperty("os.name", "UNKNOWN")
+ val OS_VERSION: String = System.getProperty("os.version", "UNKNOWN")
+ val OS_ARCH: String = System.getProperty("os.arch", "UNKNOWN")
private val newUserConf: Properties = new Properties()
private val configRenderOptions = ConfigRenderOptions.concise().setFormatted(true)
//specific highly used properties
var TimingEnabled: Boolean = false
- var aHashSettings = new HashSettingDTO(false, 0, 0, 0.0f)
- var dHashSettings = new HashSettingDTO(false, 0, 0, 0.0f)
- var pHashSettings = new HashSettingDTO(false, 0, 0, 0.0f)
- private var defaultConf: Config = null
- private var userConf: Config = null
- private var version: Version = null
+ var aHashSettings = new HashSetting("AHash", false, 0, 0, 0.0f)
+ var dHashSettings = new HashSetting("DHash", false, 0, 0, 0.0f)
+ var pHashSettings = new HashSetting("PHash", false, 0, 0, 0.0f)
+ private var defaultConf: Config = _
+ private var userConf: Config = _
+ private var version: Version = _
def getVersion: Version = this.version
@@ -47,21 +47,24 @@ object PropertiesService extends Logging {
//load special properties
TimingEnabled = get(PropertyEnum.Timed.toString).toBoolean
- aHashSettings = new HashSettingDTO(
+ aHashSettings = new HashSetting(
+ "AHash",
get(PropertyEnum.UseAhash.toString).toBoolean,
get(PropertyEnum.AhashPrecision.toString).toInt,
get(PropertyEnum.AhashTolerance.toString).toInt,
get(PropertyEnum.AhashWeight.toString).toFloat
)
- dHashSettings = new HashSettingDTO(
+ dHashSettings = new HashSetting(
+ "DHash",
get(PropertyEnum.UseDhash.toString).toBoolean,
get(PropertyEnum.DhashPrecision.toString).toInt,
get(PropertyEnum.DhashTolerance.toString).toInt,
get(PropertyEnum.DhashWeight.toString).toFloat
)
- pHashSettings = new HashSettingDTO(
+ pHashSettings = new HashSetting(
+ "PHash",
get(PropertyEnum.UsePhash.toString).toBoolean,
get(PropertyEnum.PhashPrecision.toString).toInt,
get(PropertyEnum.PhashTolerance.toString).toInt,
diff --git a/hash/pom.xml b/hash/pom.xml
index 872bd40..e864b52 100644
--- a/hash/pom.xml
+++ b/hash/pom.xml
@@ -12,7 +12,7 @@
4.0.0
hash
- 0.1.1
+ 0.2.0
jar
ImageTools-Hash
@@ -57,6 +57,11 @@
scalatest_${scala.binary.version}
test
+
+ ch.qos.logback
+ logback-classic
+ test
+
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala b/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala
index 1d88805..6230a71 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala
@@ -5,7 +5,6 @@ import java.io.{File, FileInputStream}
import javax.imageio.ImageIO
import com.sothr.imagetools.hash.`type`.{AHash, DHash, PHash}
-import com.sothr.imagetools.hash.dto.{HashSettingDTO, ImageHashDTO}
import com.sothr.imagetools.hash.util.{HammingUtil, ImageUtil}
import grizzled.slf4j.Logging
import org.apache.commons.codec.digest.DigestUtils
@@ -18,169 +17,240 @@ import resource.managed
*/
object HashService extends Logging {
- def getImageHashes(ahashSettings: HashSettingDTO,
- dhashSettings: HashSettingDTO,
- phashSettings: HashSettingDTO,
- imagePath: String): ImageHashDTO = {
- getImageHashes(ahashSettings, dhashSettings, phashSettings, ImageIO.read(new File(imagePath)), imagePath)
- }
-
- def getImageHashes(ahashSettings: HashSettingDTO,
- dhashSettings: HashSettingDTO,
- phashSettings: HashSettingDTO,
- image: BufferedImage,
- imagePath: String): ImageHashDTO = {
-
- debug(s"Creating hashes for $imagePath")
-
- var ahash: Long = 0L
- var dhash: Long = 0L
- var phash: Long = 0L
- val sha1: String = getSHA1(imagePath)
-
- //Get Image Data
- val grayImage = ImageUtil.convertToGray(image)
-
- if (ahashSettings.use) {
- ahash = getAhash(ahashSettings, grayImage, alreadyGray = true)
- }
- if (dhashSettings.use) {
- dhash = getDhash(dhashSettings, grayImage, alreadyGray = true)
- }
- if (phashSettings.use) {
- phash = getPhash(phashSettings, grayImage, alreadyGray = true)
- }
-
- val hashes = new ImageHashDTO(ahash, dhash, phash, sha1)
- debug(s"Generated hashes: $hashes")
-
- hashes
- }
-
- def getAhash(hashSettings: HashSettingDTO, image: BufferedImage, alreadyGray: Boolean = false): Long = {
- //debug("Started generating an AHash")
- var grayImage: BufferedImage = null
- if (alreadyGray) {
- grayImage = image
- } else {
- grayImage = ImageUtil.convertToGray(image)
- }
- val resizedImage = ImageUtil.resize(grayImage, hashSettings.precision, forced = true)
- val imageData = ImageUtil.getImageData(resizedImage)
- AHash.getHash(imageData)
- }
-
- def getDhash(hashSettings: HashSettingDTO, image: BufferedImage, alreadyGray: Boolean = false): Long = {
- //debug("Started generating an DHash")
- var grayImage: BufferedImage = null
- if (alreadyGray) {
- grayImage = image
- } else {
- grayImage = ImageUtil.convertToGray(image)
- }
- val resizedImage = ImageUtil.resize(grayImage, hashSettings.precision, forced = true)
- val imageData = ImageUtil.getImageData(resizedImage)
- DHash.getHash(imageData)
- }
-
- def getPhash(hashSettings: HashSettingDTO, image: BufferedImage, alreadyGray: Boolean = false): Long = {
- //debug("Started generating an PHash")
- var grayImage: BufferedImage = null
- if (alreadyGray) {
- grayImage = image
- } else {
- grayImage = ImageUtil.convertToGray(image)
- }
- val resizedImage = ImageUtil.resize(grayImage, hashSettings.precision, forced = true)
- val imageData = ImageUtil.getImageData(resizedImage)
- PHash.getHash(imageData)
- }
-
- def getSHA1(filePath: String): String = {
- managed(new FileInputStream(filePath)) acquireAndGet {
- input => DigestUtils.sha1Hex(input)
- }
- }
-
- def getMD5(filePath: String): String = {
- managed(new FileInputStream(filePath)) acquireAndGet {
- input => DigestUtils.md5Hex(input)
- }
- }
-
- def areHashSimilar(hashSettings: HashSettingDTO, hash1: Long, hash2: Long): Boolean = {
- val tolerence = hashSettings.tolerance
- val distance = HammingUtil.getDistance(hash1, hash2)
- if (distance <= tolerence) true else false
- }
-
- def areImageHashesSimilar(ahashSettings: HashSettingDTO,
- dhashSettings: HashSettingDTO,
- phashSettings: HashSettingDTO,
- imageHash1: ImageHashDTO,
- imageHash2: ImageHashDTO): Boolean = {
- val weightedHammingMean = getWeightedHashSimilarity(ahashSettings, dhashSettings, phashSettings, imageHash1, imageHash2)
- val weightedToleranceMean = getWeightedHashTolerence(ahashSettings, dhashSettings, phashSettings)
- if (weightedHammingMean <= weightedToleranceMean) true else false
- }
-
- def getWeightedHashSimilarity(ahashSettings: HashSettingDTO,
- dhashSettings: HashSettingDTO,
- phashSettings: HashSettingDTO,
- imageHash1: ImageHashDTO,
- imageHash2: ImageHashDTO): Float = {
- //calculate weighted values
- var weightedHammingTotal: Float = 0
- var methodsTotal = 0
-
- if (ahashSettings.use) {
- val hamming = HammingUtil.getDistance(imageHash1.ahash, imageHash2.ahash)
- weightedHammingTotal += hamming * ahashSettings.weight
- trace(s"hash1: ${imageHash1.ahash} hash2: ${imageHash1.ahash} tolerance: ${ahashSettings.tolerance} hamming distance: $hamming weight: ${ahashSettings.weight}")
- methodsTotal += 1
- }
- if (dhashSettings.use) {
- val hamming = HammingUtil.getDistance(imageHash1.dhash, imageHash2.dhash)
- weightedHammingTotal += hamming * dhashSettings.weight
- trace(s"hash1: ${imageHash1.dhash} hash2: ${imageHash1.dhash} tolerance: ${dhashSettings.tolerance} hamming distance: $hamming weight: ${dhashSettings.weight}")
- methodsTotal += 1
- }
- if (phashSettings.use) {
- val hamming = HammingUtil.getDistance(imageHash1.phash, imageHash2.phash)
- weightedHammingTotal += hamming * phashSettings.weight
- trace(s"hash1: ${imageHash1.phash} hash2: ${imageHash1.phash} tolerance: ${phashSettings.tolerance} hamming distance: $hamming weight: ${phashSettings.weight}")
- methodsTotal += 1
- }
- val weightedHammingMean = weightedHammingTotal / methodsTotal
- debug(s"Calculated Weighted HammingUtil Mean: $weightedHammingMean")
- weightedHammingMean
- }
-
- def getWeightedHashTolerence(ahashSettings: HashSettingDTO,
- dhashSettings: HashSettingDTO,
- phashSettings: HashSettingDTO): Float = {
- //calculate weighted values
- var weightedToleranceTotal: Float = 0
- var methodsTotal = 0
-
- if (ahashSettings.use) {
- weightedToleranceTotal += ahashSettings.tolerance * ahashSettings.weight
- trace(s"Ahash Tolerance: ${ahashSettings.tolerance} Current Weighted Tolerance: $weightedToleranceTotal")
- methodsTotal += 1
- }
- if (dhashSettings.use) {
- weightedToleranceTotal += dhashSettings.tolerance * dhashSettings.weight
- trace(s"Dhash Tolerance: ${dhashSettings.tolerance} Current Weighted Tolerance: $weightedToleranceTotal")
- methodsTotal += 1
- }
- if (phashSettings.use) {
- weightedToleranceTotal += phashSettings.tolerance * phashSettings.weight
- trace(s"Phash Tolerance: ${phashSettings.tolerance} Current Weighted Tolerance: $weightedToleranceTotal")
- methodsTotal += 1
- }
- val weightedTolerance = weightedToleranceTotal / methodsTotal
- debug(s"Calculated Weighted Tolerance: $weightedTolerance")
- weightedTolerance
- }
-
-}
+ /**
+ * Given hash settings and an image path, calculate the perceptual hashes
+ *
+ * @param ahashSettings { @see com.sothr.imagetools.hash.HashSetting}
+ * @param dhashSettings { @see com.sothr.imagetools.hash.HashSetting}
+ * @param phashSettings { @see com.sothr.imagetools.hash.HashSetting}
+ * @param imagePath Absolute path to the image file
+ * @return { @see com.sothr.imagetools.hash.ImageHash}
+ */
+ def getImageHashes(ahashSettings: HashSetting,
+ dhashSettings: HashSetting,
+ phashSettings: HashSetting,
+ imagePath: String): ImageHash = {
+ getImageHashes(ahashSettings,
+ dhashSettings,
+ phashSettings,
+ ImageIO.read(new File(imagePath)),
+ imagePath)
+ }
+
+ def getPrecisionMap(precisionSet: Set[Int], grayImage: BufferedImage): Map[Int, Array[Array[Int]]] = {
+ precisionSet.map(p => p -> getImageData(p, grayImage, alreadyGray = true))(collection.breakOut)
+ }
+
+ /**
+ * Given hash settings, a buffered image and an image path, calculate the perceptual hashes
+ *
+ * @param ahashSettings { @see com.sothr.imagetools.hash.HashSetting}
+ * @param dhashSettings { @see com.sothr.imagetools.hash.HashSetting}
+ * @param phashSettings { @see com.sothr.imagetools.hash.HashSetting}
+ * @param image { @see java.awt.image.BufferedImage}
+ * @param imagePath Absolute path to the image file
+ * @return { @see com.sothr.imagetools.hash.ImageHash}
+ *
+ */
+ def getImageHashes(ahashSettings: HashSetting,
+ dhashSettings: HashSetting,
+ phashSettings: HashSetting,
+ image: BufferedImage,
+ imagePath: String): ImageHash = {
+
+ debug(s"Creating hashes for $imagePath")
+
+ //Get Image Data
+ val grayImage = ImageUtil.convertToGray(image)
+
+ val precisionSet: Set[Int] = getPrecisionSet(ahashSettings, dhashSettings, phashSettings)
+ val precisionMap: Map[Int, Array[Array[Int]]] = getPrecisionMap(precisionSet, grayImage)
+
+ val ahash: Long = if (ahashSettings.use && precisionMap.contains(ahashSettings.precision))
+ getHash(ahashSettings, precisionMap(ahashSettings.precision), AHash.getHash)
+ else 0l
+
+ val dhash: Long = if (dhashSettings.use && precisionMap.contains(dhashSettings.precision))
+ getHash(dhashSettings, precisionMap(dhashSettings.precision), DHash.getHash)
+ else 0l
+
+ val phash: Long = if (phashSettings.use && precisionMap.contains(phashSettings.precision))
+ getHash(phashSettings, precisionMap(phashSettings.precision), PHash.getHash)
+ else 0l
+
+ val sha1: String = getSHA1(imagePath)
+
+ val hashes = new ImageHash(ahash, dhash, phash, sha1)
+ debug(s"Generated hashes: $hashes")
+
+ hashes
+ }
+
+ def getSHA1(filePath: String): String = {
+ managed(new FileInputStream(filePath)) acquireAndGet {
+ input => DigestUtils.sha1Hex(input)
+ }
+ }
+
+ def getPrecisionSet(ahashSettings: HashSetting, dhashSettings: HashSetting, phashSettings: HashSetting): Set[Int] = {
+ Set(
+ if (ahashSettings.use) Option(ahashSettings.precision) else None,
+ if (dhashSettings.use) Option(dhashSettings.precision) else None,
+ if (phashSettings.use) Option(phashSettings.precision) else None
+ ).flatten
+ }
+
+ def getImageData(precision: Int, image: BufferedImage, alreadyGray: Boolean): Array[Array[Int]] = {
+ var grayImage: BufferedImage = null
+
+ if (alreadyGray) {
+ grayImage = image
+ } else {
+ grayImage = ImageUtil.convertToGray(image)
+ }
+
+ val resizedImage = ImageUtil.resize(grayImage, precision, forced = true)
+ ImageUtil.getImageData(resizedImage)
+ }
+
+ /**
+ * Simpler function that only works with the imageData and the hashFunction
+ *
+ * @param hashSettings
+ * @param imageData
+ * @param hashFunction
+ * @return
+ */
+ def getHash(hashSettings: HashSetting, imageData: Array[Array[Int]], hashFunction: Array[Array[Int]] => Long): Long = {
+ if (hashSettings.use) {
+ val hashResult = hashFunction(imageData)
+ trace(s"${hashSettings.name} result: $hashResult")
+ hashResult
+ } else {
+ 0l
+ }
+ }
+
+ def getAhash(hashSettings: HashSetting, image: BufferedImage, alreadyGray: Boolean = false): Long = {
+ getHash(hashSettings, image, alreadyGray, AHash.getHash)
+ }
+
+ /**
+ * Internal function to retrieve the hash from a processed image, for a given hash function
+ *
+ * @param hashSettings A HashSettings that is used for the processing parameters of the Image
+ * @param image BufferedImage representing the image data to calculate the hash for
+ * @param alreadyGray Indicator that the grayscale processing has already been applied to the incoming image
+ * @param hashFunction The function accepting an two dimensional array of Int and returning the hash of the data
+ * @return
+ */
+ def getHash(hashSettings: HashSetting, image: BufferedImage, alreadyGray: Boolean = false, hashFunction: Array[Array[Int]] => Long): Long = {
+ if (hashSettings.use) {
+ var grayImage: BufferedImage = null
+
+ if (alreadyGray) {
+ grayImage = image
+ } else {
+ grayImage = ImageUtil.convertToGray(image)
+ }
+
+ val resizedImage = ImageUtil.resize(grayImage, hashSettings.precision, forced = true)
+ val hashResult = hashFunction(ImageUtil.getImageData(resizedImage))
+ trace(s"${hashSettings.name} result: $hashResult")
+ hashResult
+ } else {
+ trace(s"${hashSettings.name} result: DISABLED")
+ 0l
+ }
+ }
+
+ def getDhash(hashSettings: HashSetting, image: BufferedImage, alreadyGray: Boolean = false): Long = {
+ getHash(hashSettings, image, alreadyGray, DHash.getHash)
+ }
+
+ def getPhash(hashSettings: HashSetting, image: BufferedImage, alreadyGray: Boolean = false): Long = {
+ getHash(hashSettings, image, alreadyGray, PHash.getHash)
+ }
+
+ def getMD5(filePath: String): String = {
+ managed(new FileInputStream(filePath)) acquireAndGet {
+ input => DigestUtils.md5Hex(input)
+ }
+ }
+
+ def areHashSimilar(hashSettings: HashSetting, hash1: Long, hash2: Long): Boolean = {
+ val tolerance = hashSettings.tolerance
+ val distance = HammingUtil.getDistance(hash1, hash2)
+ if (distance <= tolerance) true else false
+ }
+
+ def areImageHashesSimilar(ahashSettings: HashSetting,
+ dhashSettings: HashSetting,
+ phashSettings: HashSetting,
+ imageHash1: ImageHash,
+ imageHash2: ImageHash): Boolean = {
+ val weightedHammingMean = getWeightedHashSimilarity(ahashSettings, dhashSettings, phashSettings, imageHash1, imageHash2)
+ val weightedToleranceMean = getWeightedHashTolerance(ahashSettings, dhashSettings, phashSettings)
+ if (weightedHammingMean <= weightedToleranceMean) true else false
+ }
+
+ def getWeightedHashSimilarity(ahashSettings: HashSetting,
+ dhashSettings: HashSetting,
+ phashSettings: HashSetting,
+ imageHash1: ImageHash,
+ imageHash2: ImageHash): Float = {
+
+ val (weightedHammingTotal1, methodsTotal1) = sumWeightedHammingAndMethodCount(ahashSettings, imageHash1.ahash, imageHash2.ahash, 0f, 0)
+ val (weightedHammingTotal2, methodsTotal2) = sumWeightedHammingAndMethodCount(dhashSettings, imageHash1.dhash, imageHash2.dhash, weightedHammingTotal1, methodsTotal1)
+ val (weightedHammingTotalFinal, methodsTotalFinal) = sumWeightedHammingAndMethodCount(phashSettings, imageHash1.phash, imageHash2.phash, weightedHammingTotal2, methodsTotal2)
+
+ val weightedHammingMean = weightedHammingTotalFinal / methodsTotalFinal
+ debug(s"Calculated Weighted HammingUtil Mean: $weightedHammingMean")
+ weightedHammingMean
+ }
+
+ def sumWeightedHammingAndMethodCount(hashSettings: HashSetting, hash1: Long, hash2: Long, weightedHamming: Float, methodCount: Int): (Float, Int) = {
+ val (newWeightedHamming, newMethodCount) = getWeightedHamming(hashSettings, hash1, hash2)
+ (weightedHamming + newWeightedHamming, methodCount + newMethodCount)
+ }
+
+ def getWeightedHamming(hashSettings: HashSetting, hash1: Long, hash2: Long): (Float, Int) = {
+ if (hashSettings.use) {
+ val hamming = HammingUtil.getDistance(hash1, hash2)
+ trace(s"${hashSettings.name} Hamming: hash1: $hash1 hash2: $hash2 tolerance: ${hashSettings.tolerance} hamming distance: $hamming weight: ${hashSettings.weight}")
+ (hamming * hashSettings.weight, 1)
+ } else {
+ trace(s"${hashSettings.name} Hamming: DISABLED")
+ (0f, 0)
+ }
+ }
+
+ def getWeightedHashTolerance(ahashSettings: HashSetting,
+ dhashSettings: HashSetting,
+ phashSettings: HashSetting): Float = {
+
+ val (weightedToleranceTotal1, methodsTotal1) = sumWeightedToleranceAndMethodCount(ahashSettings, 0f, 0)
+ val (weightedToleranceTotal2, methodsTotal2) = sumWeightedToleranceAndMethodCount(dhashSettings, weightedToleranceTotal1, methodsTotal1)
+ val (weightedToleranceTotalFinal, methodsTotalFinal) = sumWeightedToleranceAndMethodCount(phashSettings, weightedToleranceTotal2, methodsTotal2)
+
+ val weightedTolerance = weightedToleranceTotalFinal / methodsTotalFinal
+ debug(s"Calculated Weighted Tolerance: $weightedTolerance")
+ weightedTolerance
+ }
+
+ def sumWeightedToleranceAndMethodCount(hashSettings: HashSetting, weightedTolerance: Float, methodCount: Int): (Float, Int) = {
+ val (newWeightedTolerance, newMethodCount) = getWeightedTolerance(hashSettings)
+ (weightedTolerance + newWeightedTolerance, methodCount + newMethodCount)
+ }
+
+ def getWeightedTolerance(hashSettings: HashSetting): (Float, Int) = {
+ if (hashSettings.use) {
+ val weightedTolerance = hashSettings.tolerance * hashSettings.weight
+ trace(s"${hashSettings.name} Tolerance: ${hashSettings.tolerance} Current Weighted Tolerance: $weightedTolerance")
+ (weightedTolerance, 1)
+ } else {
+ trace(s"${hashSettings.name} Tolerance: DISABLED")
+ (0f, 0)
+ }
+ }
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/HashSetting.scala b/hash/src/main/scala/com/sothr/imagetools/hash/HashSetting.scala
new file mode 100644
index 0000000..a37e2c6
--- /dev/null
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/HashSetting.scala
@@ -0,0 +1,8 @@
+package com.sothr.imagetools.hash
+
+class HashSetting(val name: String,
+ val use: Boolean,
+ val precision: Int,
+ val tolerance: Int,
+ val weight: Float) {
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/ImageHash.scala b/hash/src/main/scala/com/sothr/imagetools/hash/ImageHash.scala
new file mode 100644
index 0000000..bc628b0
--- /dev/null
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/ImageHash.scala
@@ -0,0 +1,25 @@
+package com.sothr.imagetools.hash
+
+import grizzled.slf4j.Logging
+
+class ImageHash(val ahash: Long,
+ val dhash: Long,
+ val phash: Long,
+ val fileHash: String) extends Serializable with Logging {
+
+ def cloneHashes: ImageHash = {
+ new ImageHash(ahash, dhash, phash, fileHash)
+ }
+
+ override def hashCode(): Int = {
+ var result = 365
+ result = 41 * result + (this.ahash ^ (this.ahash >>> 32)).toInt
+ result = 37 * result + (this.dhash ^ (this.dhash >>> 32)).toInt
+ result = 2 * result + (this.phash ^ (this.phash >>> 32)).toInt
+ result
+ }
+
+ override def toString: String = {
+ s"fileHash: $fileHash ahash: $ahash dhash: $dhash phash: $phash"
+ }
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/dto/HashSettingDTO.scala b/hash/src/main/scala/com/sothr/imagetools/hash/dto/HashSettingDTO.scala
deleted file mode 100644
index eb9e947..0000000
--- a/hash/src/main/scala/com/sothr/imagetools/hash/dto/HashSettingDTO.scala
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.sothr.imagetools.hash.dto
-
-class HashSettingDTO(
- val use: Boolean,
- val precision: Int,
- val tolerance: Int,
- val weight: Float) {
-}
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/dto/ImageHashDTO.scala b/hash/src/main/scala/com/sothr/imagetools/hash/dto/ImageHashDTO.scala
deleted file mode 100644
index acad5fd..0000000
--- a/hash/src/main/scala/com/sothr/imagetools/hash/dto/ImageHashDTO.scala
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.sothr.imagetools.hash.dto
-
-import grizzled.slf4j.Logging
-
-class ImageHashDTO(var ahash: Long, var dhash: Long, var phash: Long, var fileHash: String) extends Serializable with Logging {
-
- def getAhash: Long = ahash
-
- def setAhash(hash: Long) = {
- ahash = hash
- }
-
- def getDhash: Long = dhash
-
- def setDhash(hash: Long) = {
- dhash = hash
- }
-
- def getPhash: Long = phash
-
- def setPhash(hash: Long) = {
- phash = hash
- }
-
- def getFileHash: String = fileHash
-
- def setFileHash(hash: String) = {
- fileHash = hash
- }
-
- def cloneHashes: ImageHashDTO = {
- new ImageHashDTO(ahash, dhash, phash, fileHash)
- }
-
- override def hashCode(): Int = {
- var result = 365
- result = 41 * result + (this.ahash ^ (this.ahash >>> 32)).toInt
- result = 37 * result + (this.dhash ^ (this.dhash >>> 32)).toInt
- result = 2 * result + (this.phash ^ (this.phash >>> 32)).toInt
- result
- }
-
- override def toString: String = {
- s"fileHash: $fileHash ahash: $ahash dhash: $dhash phash: $phash"
- }
-}
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/type/AHash.scala b/hash/src/main/scala/com/sothr/imagetools/hash/type/AHash.scala
index e211a8b..94c5547 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/type/AHash.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/type/AHash.scala
@@ -3,49 +3,50 @@ package com.sothr.imagetools.hash.`type`
import grizzled.slf4j.Logging
/**
- * Speedy AHash Perceptual Hashing
- * Uses the average value of the pixels as a baseline
- *
- * Created by dev on 1/22/14.
- */
+ * Speedy AHash Perceptual Hashing
+ * Uses the average value of the pixels as a baseline to determine the structure of the image
+ *
+ * Created by dev on 1/22/14.
+ */
object AHash extends PerceptualHasher with Logging {
- def getHash(imageData: Array[Array[Int]]): Long = {
- trace("Generating AHash")
- val width = imageData.length
- val height = imageData(0).length
- trace(s"Image data size: ${width}x$height")
+ def getHash(imageData: Array[Array[Int]]): Long = {
+ trace("Generating AHash")
+ val width = imageData.length
+ val height = imageData(0).length
+ trace(s"Image data size: ${width}x$height")
- //calculate average pixel
- var total = 0
- for (row <- 0 until height) {
- for (col <- 0 until width) {
- total += imageData(row)(col)
- }
- }
- val mean = total / (height * width)
+ /*
+ Average Pixel Calculation
+ */
+ var total = 0
+ for (row <- 0 until height) {
+ for (col <- 0 until width) {
+ total += imageData(row)(col)
+ }
+ }
+ val mean = total / (height * width)
- //calculate ahash
- var hash = 0L
- for (row <- 0 until height by 2) {
- //process each column
- for (col <- 0 until width by 1) {
- hash <<= 1
- val pixel = imageData(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
- }
+ /*
+ For each pixel, if it is at or above the average, store it as a one, else store it as a zero
+ */
+ var hash = 0L
+ for (row <- 0 until height by 2) {
+ for (col <- 0 until width by 1) {
+ hash <<= 1
+ val pixel = imageData(row)(col)
+ if (pixel >= mean) hash |= 1 else hash |= 0
+ }
- if ((row + 1) < width) {
- val nextRow = row + 1
- //process each column
- for (col <- (width - 1) to 0 by -1) {
- hash <<= 1
- val pixel = imageData(nextRow)(col)
- if (pixel >= mean) hash |= 1 else hash |= 0
- }
- }
- }
- debug(s"Computed AHash: $hash from ${width * height} pixels")
- hash
- }
-}
+ if ((row + 1) < width) {
+ val nextRow = row + 1
+ for (col <- (width - 1) to 0 by -1) {
+ hash <<= 1
+ val pixel = imageData(nextRow)(col)
+ if (pixel >= mean) hash |= 1 else hash |= 0
+ }
+ }
+ }
+ debug(s"Computed AHash: $hash from ${width * height} pixels")
+ hash
+ }
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/type/DHash.scala b/hash/src/main/scala/com/sothr/imagetools/hash/type/DHash.scala
index d2854b6..44317dd 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/type/DHash.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/type/DHash.scala
@@ -3,55 +3,57 @@ package com.sothr.imagetools.hash.`type`
import grizzled.slf4j.Logging
/**
- * DHash algorithm class
- *
- * Created by Drew on 1/22/14.
- */
+ * DHash algorithm object
+ *
+ * Tracks the differences between pixels in a pattern as the basis of similarity
+ *
+ * Created by Drew on 1/22/14.
+ */
object DHash extends PerceptualHasher with Logging {
- def getHash(imageData: Array[Array[Int]]): Long = {
- trace("Generating DHash")
- val width = imageData.length
- val height = imageData(0).length
- trace(s"Image data size: ${width}x$height")
+ def getHash(imageData: Array[Array[Int]]): Long = {
+ trace("Generating DHash")
+ val width = imageData.length
+ val height = imageData(0).length
+ trace(s"Image data size: ${width}x$height")
- //calculate dhash
- var hash = 0L
- var previousPixel = imageData(height - 1)(width - 1)
- var previousLocation = (height - 1, width - 1)
- if (height % 2 == 0) {
- previousPixel = imageData(height - 1)(0)
- previousLocation = (height - 1, 0)
- }
+ //calculate dhash
+ var hash = 0L
+ var previousPixel = imageData(height - 1)(width - 1)
+ var previousLocation = (height - 1, width - 1)
+ if (height % 2 == 0) {
+ previousPixel = imageData(height - 1)(0)
+ previousLocation = (height - 1, 0)
+ }
- for (row <- 0 until height by 2) {
- //process each column
- for (col <- 0 until width by 1) {
- hash <<= 1
- val pixel = imageData(row)(col)
- //debug(s"previousPixel: $previousPixel currentPixel: $pixel previousLocation: $previousLocation currentLocation: (${row},${col})")
- //binary of the current bit based on whether the value
- //of the current pixel is greater or equal to the previous pixel
- if (pixel >= previousPixel) hash |= 1 else hash |= 0
- //debug(s"(${row},${col})=$pixel hash=${hash.toBinaryString}")
- previousPixel = pixel
- previousLocation = (row, col)
- }
+ for (row <- 0 until height by 2) {
+ //process each column
+ for (col <- 0 until width by 1) {
+ hash <<= 1
+ val pixel = imageData(row)(col)
+ //debug(s"previousPixel: $previousPixel currentPixel: $pixel previousLocation: $previousLocation currentLocation: (${row},${col})")
+ //binary of the current bit based on whether the value
+ //of the current pixel is greater or equal to the previous pixel
+ if (pixel >= previousPixel) hash |= 1 else hash |= 0
+ //debug(s"(${row},${col})=$pixel hash=${hash.toBinaryString}")
+ previousPixel = pixel
+ previousLocation = (row, col)
+ }
- if ((row + 1) < width) {
- val nextRow = row + 1
- //process each column
- for (col <- (width - 1) to 0 by -1) {
- hash <<= 1
- val pixel = imageData(nextRow)(col)
- //debug(s"previousPixel: $previousPixel currentPixel: $pixel previousLocation: $previousLocation currentLocation: (${nextRow},${col})")
- if (pixel >= previousPixel) hash |= 1 else hash |= 0
- //debug(s"(${row},${col})=$pixel hash=${hash.toBinaryString}")
- previousPixel = pixel
- previousLocation = (nextRow, col)
- }
- }
- }
- debug(s"Computed DHash: $hash from ${width * height} pixels")
- hash
- }
-}
+ if ((row + 1) < width) {
+ val nextRow = row + 1
+ //process each column
+ for (col <- (width - 1) to 0 by -1) {
+ hash <<= 1
+ val pixel = imageData(nextRow)(col)
+ //debug(s"previousPixel: $previousPixel currentPixel: $pixel previousLocation: $previousLocation currentLocation: (${nextRow},${col})")
+ if (pixel >= previousPixel) hash |= 1 else hash |= 0
+ //debug(s"(${row},${col})=$pixel hash=${hash.toBinaryString}")
+ previousPixel = pixel
+ previousLocation = (nextRow, col)
+ }
+ }
+ }
+ debug(s"Computed DHash: $hash from ${width * height} pixels")
+ hash
+ }
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/type/PHash.scala b/hash/src/main/scala/com/sothr/imagetools/hash/type/PHash.scala
index 5660974..137c9fb 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/type/PHash.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/type/PHash.scala
@@ -4,69 +4,66 @@ import edu.emory.mathcs.jtransforms.dct.FloatDCT_2D
import grizzled.slf4j.Logging
/**
- * Complex perceptual hash
- * Uses FFT to get
- *
- * Created by dev on 1/22/14.
- */
+ * Complex perceptual hash
+ * Uses FFT to get structural data out of the image, which can represent the perception of similarity
+ *
+ * Created by dev on 1/22/14.
+ */
object PHash extends PerceptualHasher with Logging {
- def getHash(imageData: Array[Array[Int]]): Long = {
- //convert the imageData into a FloatArray
- val width = imageData.length
- val height = imageData(0).length
- trace(s"Starting with image of ${height}x$width for PHash")
+ def getHash(imageData: Array[Array[Int]]): Long = {
+ //convert the imageData into a FloatArray
+ val width = imageData.length
+ val height = imageData(0).length
+ trace(s"Starting with image of ${height}x$width for PHash")
- 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
- }
- }
- //debug("Copied image data to float array for transform")
- //debug(s"\n${imageDataFloat.deep.mkString("\n")}")
+ 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)
- //debug("Converted image data into DCT")
- //debug(s"\n${imageDataFloat.deep.mkString("\n")}")
+ //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
+ //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)
- //debug(s"Calculated mean as $mean from ${total}/${dctDataHeight * dctDataWidth}")
+ //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)
+ //debug(s"Calculated mean as $mean from ${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
- }
+ /*
+ For each pixel, if it is at or above the mean, store it as a one, else store it as a zero
+ */
+ var hash = 0L
+ for (row <- 0 until dctDataHeight by 2) {
+ for (col <- 0 until dctDataWidth by 1) {
+ hash <<= 1
+ val pixel = imageDataFloat(row)(col)
+ //
+ 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
- }
-}
+ 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
+ }
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/type/PerceptualHasher.scala b/hash/src/main/scala/com/sothr/imagetools/hash/type/PerceptualHasher.scala
index 9af8b3a..ee168fa 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/type/PerceptualHasher.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/type/PerceptualHasher.scala
@@ -1,12 +1,12 @@
package com.sothr.imagetools.hash.`type`
/**
- * Interface for perceptual hashing
- *
- * Created by drew on 1/22/14.
- */
+ * Interface for perceptual hashing
+ *
+ * Created by drew on 1/22/14.
+ */
trait PerceptualHasher {
- def getHash(imageData: Array[Array[Int]]): Long
+ def getHash(imageData: Array[Array[Int]]): Long
-}
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/util/HammingUtil.scala b/hash/src/main/scala/com/sothr/imagetools/hash/util/HammingUtil.scala
index 2651471..c8023d5 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/util/HammingUtil.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/util/HammingUtil.scala
@@ -2,17 +2,16 @@ package com.sothr.imagetools.hash.util
object HammingUtil {
- /**
- * Calculate the hamming distance between two longs
- *
- * @param hash1 The first hash to compare
- * @param hash2 The second hash to compare
- * @return
- */
- def getDistance(hash1: Long, hash2: Long): Int = {
- //The XOR of hash1 and hash2 is converted to a binary string
- //then the number of '1's is counted. This is the hamming distance
- (hash1 ^ hash2).toBinaryString.count(_ == '1')
- }
-
+ /**
+ * Calculate the hamming distance between two longs
+ *
+ * @param hash1 The first hash to compare
+ * @param hash2 The second hash to compare
+ * @return
+ */
+ def getDistance(hash1: Long, hash2: Long): Int = {
+ //The XOR of hash1 and hash2 is converted to a binary string
+ //then the number of '1's is counted. This is the hamming distance
+ (hash1 ^ hash2).toBinaryString.count(_ == '1')
+ }
}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/util/ImageUtil.scala b/hash/src/main/scala/com/sothr/imagetools/hash/util/ImageUtil.scala
index 38f7fd3..e63a285 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/util/ImageUtil.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/util/ImageUtil.scala
@@ -7,111 +7,110 @@ import net.coobird.thumbnailator.Thumbnails
object ImageUtil extends Logging {
- /**
- * Quickly convert an image to grayscale
- *
- * @param image image to convert to greyscale
- * @return
- */
- def convertToGray(image: BufferedImage): BufferedImage = {
- val grayImage = new BufferedImage(image.getWidth, image.getHeight, BufferedImage.TYPE_BYTE_GRAY)
+ /**
+ * Quickly convert an image to grayscale
+ *
+ * @param image image to convert to greyscale
+ * @return
+ */
+ def convertToGray(image: BufferedImage): BufferedImage = {
+ val grayImage = new BufferedImage(image.getWidth, image.getHeight, BufferedImage.TYPE_BYTE_GRAY)
- val op = new ColorConvertOp(
- image.getColorModel.getColorSpace,
- grayImage.getColorModel.getColorSpace,
- null
- )
+ val op = new ColorConvertOp(
+ image.getColorModel.getColorSpace,
+ grayImage.getColorModel.getColorSpace,
+ null
+ )
- op.filter(image, grayImage)
- }
+ op.filter(image, grayImage)
+ }
- def resize(image: BufferedImage, size: Int, forced: Boolean = false): BufferedImage = {
- //debug(s"Resizing an image to size: ${size}x${size} forced: $forced")
- if (forced) {
- Thumbnails.of(image).forceSize(size, size).asBufferedImage
- } else {
- Thumbnails.of(image).size(size, size).asBufferedImage
- }
- }
+ def resize(image: BufferedImage, size: Int, forced: Boolean = false): BufferedImage = {
+ //debug(s"Resizing an image to size: ${size}x${size} forced: $forced")
+ if (forced) {
+ Thumbnails.of(image).forceSize(size, size).asBufferedImage
+ } else {
+ Thumbnails.of(image).size(size, size).asBufferedImage
+ }
+ }
- /**
- * Get the raw data for an image
- * Convert a buffered image into a 2d pixel data array
- *
- * @param image image to convert without using RGB
- * @return
- */
- def getImageData(image: BufferedImage): Array[Array[Int]] = {
+ /**
+ * Get the raw data for an image
+ * Convert a buffered image into a 2d pixel data array
+ *
+ * @param image image to convert without using RGB
+ * @return
+ */
+ def getImageData(image: BufferedImage): Array[Array[Int]] = {
- val pixels = image.getRaster.getDataBuffer.asInstanceOf[DataBufferByte].getData
- val numPixels = pixels.length
- val width = image.getWidth
- val height = image.getHeight
- val isSingleChannel = if (numPixels == (width * height)) true else false
- val hasAlphaChannel = image.getAlphaRaster != null
- //debug(s"Converting image to 2d. width:$width height:$height")
+ val pixels = image.getRaster.getDataBuffer.asInstanceOf[DataBufferByte].getData
+ val numPixels = pixels.length
+ val width = image.getWidth
+ val height = image.getHeight
+ val isSingleChannel = if (numPixels == (width * height)) true else false
+ val hasAlphaChannel = image.getAlphaRaster != null
+ //debug(s"Converting image to 2d. width:$width height:$height")
- val result = Array.ofDim[Int](height, width)
- if (isSingleChannel) {
- //debug(s"Processing Single Channel Image")
- val pixelLength = 1
- var row = 0
- var col = 0
- //debug(s"Processing pixels 0 until $numPixels by $pixelLength")
- for (pixel <- 0 until numPixels by pixelLength) {
- //debug(s"Processing pixel: $pixel/${numPixels - 1}")
- val argb: Int = pixels(pixel).toInt //singleChannel
- //debug(s"Pixel data: $argb")
- result(row)(col) = argb
- col += 1
- if (col == width) {
- col = 0
- row += 1
- }
- }
- }
- else if (hasAlphaChannel) {
- //debug(s"Processing Four Channel Image")
- val pixelLength = 4
- var row = 0
- var col = 0
- //debug(s"Processing pixels 0 until $numPixels by $pixelLength")
- for (pixel <- 0 until numPixels by pixelLength) {
- //debug(s"Processing pixel: $pixel/${numPixels - 1}")
- var argb: Int = 0
- argb += pixels(pixel).toInt << 24 //alpha
- argb += pixels(pixel + 1).toInt //blue
- argb += pixels(pixel + 2).toInt << 8 //green
- argb += pixels(pixel + 3).toInt << 16 //red
- result(row)(col) = argb
- col += 1
- if (col == width) {
- col = 0
- row += 1
- }
- }
- } else {
- //debug(s"Processing Three Channel Image")
- val pixelLength = 3
- var row = 0
- var col = 0
- //debug(s"Processing pixels 0 until $numPixels by $pixelLength")
- for (pixel <- 0 until numPixels by pixelLength) {
- //debug(s"Processing pixel: $pixel/${numPixels - 1}")
- var argb: Int = 0
- argb += -16777216; // 255 alpha
- argb += pixels(pixel).toInt //blue
- argb += pixels(pixel + 1).toInt << 8 //green
- argb += pixels(pixel + 2).toInt << 16 //red
- result(row)(col) = argb
- col += 1
- if (col == width) {
- col = 0
- row += 1
- }
- }
- }
- result
- }
-
-}
+ val result = Array.ofDim[Int](height, width)
+ if (isSingleChannel) {
+ //debug(s"Processing Single Channel Image")
+ val pixelLength = 1
+ var row = 0
+ var col = 0
+ //debug(s"Processing pixels 0 until $numPixels by $pixelLength")
+ for (pixel <- 0 until numPixels by pixelLength) {
+ //debug(s"Processing pixel: $pixel/${numPixels - 1}")
+ val argb: Int = pixels(pixel).toInt //singleChannel
+ //debug(s"Pixel data: $argb")
+ result(row)(col) = argb
+ col += 1
+ if (col == width) {
+ col = 0
+ row += 1
+ }
+ }
+ }
+ else if (hasAlphaChannel) {
+ //debug(s"Processing Four Channel Image")
+ val pixelLength = 4
+ var row = 0
+ var col = 0
+ //debug(s"Processing pixels 0 until $numPixels by $pixelLength")
+ for (pixel <- 0 until numPixels by pixelLength) {
+ //debug(s"Processing pixel: $pixel/${numPixels - 1}")
+ var argb: Int = 0
+ argb += pixels(pixel).toInt << 24 //alpha
+ argb += pixels(pixel + 1).toInt //blue
+ argb += pixels(pixel + 2).toInt << 8 //green
+ argb += pixels(pixel + 3).toInt << 16 //red
+ result(row)(col) = argb
+ col += 1
+ if (col == width) {
+ col = 0
+ row += 1
+ }
+ }
+ } else {
+ //debug(s"Processing Three Channel Image")
+ val pixelLength = 3
+ var row = 0
+ var col = 0
+ //debug(s"Processing pixels 0 until $numPixels by $pixelLength")
+ for (pixel <- 0 until numPixels by pixelLength) {
+ //debug(s"Processing pixel: $pixel/${numPixels - 1}")
+ var argb: Int = 0
+ argb += -16777216; // 255 alpha
+ argb += pixels(pixel).toInt //blue
+ argb += pixels(pixel + 1).toInt << 8 //green
+ argb += pixels(pixel + 2).toInt << 16 //red
+ result(row)(col) = argb
+ col += 1
+ if (col == width) {
+ col = 0
+ row += 1
+ }
+ }
+ }
+ result
+ }
+}
\ No newline at end of file
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/util/TimingUtil.scala b/hash/src/main/scala/com/sothr/imagetools/hash/util/TimingUtil.scala
index d566032..78a8efe 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/util/TimingUtil.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/util/TimingUtil.scala
@@ -4,32 +4,31 @@ import grizzled.slf4j.Logging
trait TimingUtil extends Logging {
- def time[R](block: => R): R = {
- val t0 = System.currentTimeMillis
- val result = block // call-by-name
- val t1 = System.currentTimeMillis
- debug("Elapsed time: " + (t1 - t0) + "ms")
- result
- }
+ def time[R](block: => R): R = {
+ val t0 = System.currentTimeMillis
+ val result = block // call-by-name
+ val t1 = System.currentTimeMillis
+ debug("Elapsed time: " + (t1 - t0) + "ms")
+ result
+ }
- def getTime[R](block: => R): Long = {
- val t0 = System.currentTimeMillis
- val result = block // call-by-name
- val t1 = System.currentTimeMillis
- debug("Elapsed time: " + (t1 - t0) + "ms")
- t1 - t0
- }
+ def getTime[R](block: => R): Long = {
+ val t0 = System.currentTimeMillis
+ val result = block // call-by-name
+ val t1 = System.currentTimeMillis
+ debug("Elapsed time: " + (t1 - t0) + "ms")
+ t1 - t0
+ }
- def getMean(times: Long*): Long = {
- getMean(times.toArray[Long])
- }
-
- def getMean(times: Array[Long]): Long = {
- var ag = 0L
- for (i <- times.indices) {
- ag += times(i)
- }
- ag / times.length
- }
+ def getMean(times: Long*): Long = {
+ getMean(times.toArray[Long])
+ }
+ def getMean(times: Array[Long]): Long = {
+ var ag = 0L
+ for (i <- times.indices) {
+ ag += times(i)
+ }
+ ag / times.length
+ }
}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/BaseTest.scala b/hash/src/test/scala/com/sothr/imagetools/hash/BaseTest.scala
deleted file mode 100644
index 11e7b7c..0000000
--- a/hash/src/test/scala/com/sothr/imagetools/hash/BaseTest.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.sothr.imagetools.hash
-
-import com.sothr.imagetools.hash.util.TimingUtil
-import grizzled.slf4j.Logging
-import org.scalatest.{BeforeAndAfter, FunSuite, Inside, Inspectors, Matchers, OptionValues}
-
-abstract class BaseTest extends FunSuite with Matchers with OptionValues with Inside with Inspectors with BeforeAndAfter with Logging with TimingUtil {
-
-}
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceAHashTest.scala b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceAHashTest.scala
new file mode 100644
index 0000000..da7ee71
--- /dev/null
+++ b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceAHashTest.scala
@@ -0,0 +1,93 @@
+package com.sothr.imagetools.hash
+
+import java.io.File
+import javax.imageio.ImageIO
+
+import scala.collection.mutable
+
+class HashServiceAHashTest extends HashServiceBaseTest {
+
+ def ahashTestCase(filePath: String): Long = {
+ val sample = new File(filePath)
+ val image = ImageIO.read(sample)
+ HashService.getAhash(ahashSetting, image)
+ }
+
+ test("Calculate AHash Large Sample Image 1") {
+ debug("Starting 'Calculate AHash Large Sample Image 1' test")
+ val sample = new File(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(ahashSetting, 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(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(ahashSetting, 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(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(ahashSetting, 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(LargeSampleImage1)
+ val mediumHash = ahashTestCase(MediumSampleImage1)
+ val smallHash = ahashTestCase(SmallSampleImage1)
+ assert(HashService.areHashSimilar(ahashSetting, largeHash, mediumHash))
+ assert(HashService.areHashSimilar(ahashSetting, largeHash, smallHash))
+ assert(HashService.areHashSimilar(ahashSetting, mediumHash, smallHash))
+ }
+
+ test("Benchmark AHash") {
+ info("Benchmarking AHash")
+ info("AHash Large Image 3684x2736")
+ val time = new mutable.MutableList[Long]()
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ ahashTestCase(LargeSampleImage1)
+ }
+ }
+
+ val largeMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
+ time.clear()
+ info("AHash Medium Image 1824x1368")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ ahashTestCase(MediumSampleImage1)
+ }
+ }
+
+ val mediumMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
+ time.clear()
+ info("AHash Small Image 912x684")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ ahashTestCase(SmallSampleImage1)
+ }
+ }
+
+ val smallMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
+ time.clear()
+ assert(true)
+ }
+}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceBaseTest.scala b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceBaseTest.scala
new file mode 100644
index 0000000..16967b4
--- /dev/null
+++ b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceBaseTest.scala
@@ -0,0 +1,21 @@
+package com.sothr.imagetools.hash
+
+import com.sothr.imagetools.hash.util.TimingUtil
+import grizzled.slf4j.Logging
+import org.scalatest.{BeforeAndAfter, FunSuite, Inside, Inspectors, Matchers, OptionValues}
+
+abstract class HashServiceBaseTest extends FunSuite with Matchers with OptionValues with Inside with Inspectors with BeforeAndAfter with Logging with TimingUtil {
+
+ // Define the number of runs the benchmarking tests should use
+ val benchmarkRuns = 10
+
+ // The Standard Parameters Used To Benchmark Perceptual hashes
+ val ahashSetting = new HashSetting("AHash", true, 8, 8, 0.75f)
+ val dhashSetting = new HashSetting("DHash", true, 8, 8, 0.85f)
+ val phashSetting = new HashSetting("PHash", true, 32, 8, 1.0f)
+
+ // Base Test Images
+ val LargeSampleImage1 = "sample/sample_01_large.jpg"
+ val MediumSampleImage1 = "sample/sample_01_medium.jpg"
+ val SmallSampleImage1 = "sample/sample_01_small.jpg"
+}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceDHashTest.scala b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceDHashTest.scala
new file mode 100644
index 0000000..1d62ade
--- /dev/null
+++ b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceDHashTest.scala
@@ -0,0 +1,110 @@
+package com.sothr.imagetools.hash
+
+import java.io.File
+import javax.imageio.ImageIO
+
+import com.sothr.imagetools.hash.`type`.DHash
+
+import scala.collection.mutable
+
+class HashServiceDHashTest extends HashServiceBaseTest {
+
+ def dhashTestCase(filePath: String): Long = {
+ val sample = new File(filePath)
+ val image = ImageIO.read(sample)
+ HashService.getDhash(dhashSetting, image)
+ }
+
+ test("Confirm Largest DHash Output ") {
+ val testData: Array[Array[Int]] = Array(
+ Array(1, 2, 3, 4, 5, 6, 7, 8),
+ Array(16, 15, 14, 13, 12, 11, 10, 9),
+ Array(17, 18, 19, 20, 21, 22, 23, 24),
+ Array(32, 31, 30, 29, 28, 27, 26, 25),
+ Array(33, 34, 35, 36, 37, 38, 39, 40),
+ Array(48, 47, 46, 45, 44, 43, 42, 41),
+ Array(49, 50, 51, 52, 53, 54, 55, 56),
+ Array(64, 63, 62, 61, 60, 59, 58, 57))
+ val hash = DHash.getHash(testData)
+ debug(s"Hash of test array: $hash")
+ assert(hash == Long.MaxValue)
+ }
+
+ test("Calculate DHash Large Sample Image 1") {
+ debug("Starting 'Calculate DHash Large Sample Image 1' test")
+ val sample = new File(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.getDhash(dhashSetting, image)
+ debug(s"Testing that $hash = 4004374827879799635L")
+ assert(hash == 4004374827879799635L)
+ }
+
+ test("Calculate DHash Medium Sample Image 1") {
+ debug("Starting 'Calculate DHash Medium Sample Image 1' test")
+ val sample = new File(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.getDhash(dhashSetting, image)
+ debug(s"Testing that $hash = 4004374827879799635L")
+ assert(hash == 4004374827879799635L)
+ }
+
+ test("Calculate DHash Small Sample Image 1") {
+ debug("Starting 'Calculate DHash Small Sample Image 1' test")
+ val sample = new File(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.getDhash(dhashSetting, image)
+ debug(s"Testing that $hash = 4004383623972821843L")
+ assert(hash == 4004383623972821843L)
+ }
+
+ test("DHash Of Large, Medium, And Small Sample 1 Must Be Similar") {
+ val largeHash = dhashTestCase(LargeSampleImage1)
+ val mediumHash = dhashTestCase(MediumSampleImage1)
+ val smallHash = dhashTestCase(SmallSampleImage1)
+ assert(HashService.areHashSimilar(dhashSetting, largeHash, mediumHash))
+ assert(HashService.areHashSimilar(dhashSetting, largeHash, smallHash))
+ assert(HashService.areHashSimilar(dhashSetting, mediumHash, smallHash))
+ }
+
+ test("Benchmark DHash") {
+ info("Benchmarking DHash")
+ info("DHash Large Image 3684x2736")
+ val time = new mutable.MutableList[Long]()
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ dhashTestCase(LargeSampleImage1)
+ }
+ }
+
+ val largeMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
+ time.clear()
+ info("DHash Medium Image 1824x1368")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ dhashTestCase(MediumSampleImage1)
+ }
+ }
+
+ val mediumMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
+ time.clear()
+ info("DHash Small Image 912x684")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ dhashTestCase(SmallSampleImage1)
+ }
+ }
+
+ val smallMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
+ time.clear()
+ assert(true)
+ }
+}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceMD5Test.scala b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceMD5Test.scala
new file mode 100644
index 0000000..f95a9e2
--- /dev/null
+++ b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceMD5Test.scala
@@ -0,0 +1,67 @@
+package com.sothr.imagetools.hash
+
+import scala.collection.mutable
+
+class HashServiceMD5Test extends HashServiceBaseTest {
+
+ def md5TestCase(filePath: String): String = {
+ HashService.getMD5(filePath)
+ }
+
+ test("Calculate MD5 Large Sample Image 1") {
+ debug("Starting 'Calculate MD5 Large Sample Image 1' test")
+ val hash = HashService.getMD5(LargeSampleImage1)
+ debug(s"Testing that $hash = 3fbccfd5faf3f991435b827ee5961862")
+ assert(hash == "3fbccfd5faf3f991435b827ee5961862")
+ }
+
+ test("Calculate MD5 Medium Sample Image 1") {
+ debug("Starting 'Calculate MD5 Medium Sample Image 1' test")
+ val hash = HashService.getMD5(MediumSampleImage1)
+ debug(s"Testing that $hash = a95e2cc4610307eb957e9c812429c53e")
+ assert(hash == "a95e2cc4610307eb957e9c812429c53e")
+ }
+
+ test("Calculate MD5 Small Sample Image 1") {
+ debug("Starting 'Calculate MD5 Small Sample Image 1' test")
+ val hash = HashService.getMD5(SmallSampleImage1)
+ debug(s"Testing that $hash = b137131bd55896c747286e4d247b845e")
+ assert(hash == "b137131bd55896c747286e4d247b845e")
+ }
+
+ test("Benchmark MD5") {
+ info("Benchmarking MD5")
+ info("MD5 Large Image 3684x2736")
+ val time = new mutable.MutableList[Long]()
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ md5TestCase(LargeSampleImage1)
+ }
+ }
+
+ val largeMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
+ time.clear()
+ info("MD5 Medium Image 1824x1368")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ md5TestCase(MediumSampleImage1)
+ }
+ }
+
+ val mediumMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
+ time.clear()
+ info("MD5 Small Image 912x684")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ md5TestCase(SmallSampleImage1)
+ }
+ }
+
+ val smallMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
+ time.clear()
+ assert(true)
+ }
+}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/HashServicePHashTest.scala b/hash/src/test/scala/com/sothr/imagetools/hash/HashServicePHashTest.scala
new file mode 100644
index 0000000..3650eab
--- /dev/null
+++ b/hash/src/test/scala/com/sothr/imagetools/hash/HashServicePHashTest.scala
@@ -0,0 +1,93 @@
+package com.sothr.imagetools.hash
+
+import java.io.File
+import javax.imageio.ImageIO
+
+import scala.collection.mutable
+
+class HashServicePHashTest extends HashServiceBaseTest {
+
+ def phashTestCase(filePath: String): Long = {
+ val sample = new File(filePath)
+ val image = ImageIO.read(sample)
+ HashService.getPhash(phashSetting, image)
+ }
+
+ test("Calculate PHash Large Sample Image 1") {
+ debug("Starting 'Calculate PHash Large Sample Image 1' test")
+ val sample = new File(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(phashSetting, image)
+ debug(s"Testing that $hash = -9154554603604154117L")
+ assert(hash == -9154554603604154117L)
+ }
+
+ test("Calculate PHash Medium Sample Image 1") {
+ debug("Starting 'Calculate PHash Medium Sample Image 1' test")
+ val sample = new File(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(phashSetting, 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(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(phashSetting, 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(LargeSampleImage1)
+ val mediumHash = phashTestCase(MediumSampleImage1)
+ val smallHash = phashTestCase(SmallSampleImage1)
+ assert(HashService.areHashSimilar(phashSetting, largeHash, mediumHash))
+ assert(HashService.areHashSimilar(phashSetting, largeHash, smallHash))
+ assert(HashService.areHashSimilar(phashSetting, mediumHash, smallHash))
+ }
+
+ test("Benchmark PHash") {
+ info("Benchmarking PHash")
+ info("PHash Large Image 3684x2736")
+ val time = new mutable.MutableList[Long]()
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ phashTestCase(LargeSampleImage1)
+ }
+ }
+
+ val largeMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
+ time.clear()
+ info("PHash Medium Image 1824x1368")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ phashTestCase(MediumSampleImage1)
+ }
+ }
+
+ val mediumMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
+ time.clear()
+ info("PHash Small Image 912x684")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ phashTestCase(SmallSampleImage1)
+ }
+ }
+
+ val smallMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
+ time.clear()
+ assert(true)
+ }
+}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceSHA1Test.scala b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceSHA1Test.scala
new file mode 100644
index 0000000..e44a063
--- /dev/null
+++ b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceSHA1Test.scala
@@ -0,0 +1,67 @@
+package com.sothr.imagetools.hash
+
+import scala.collection.mutable
+
+class HashServiceSHA1Test extends HashServiceBaseTest {
+
+ def sha1TestCase(filePath: String): String = {
+ HashService.getSHA1(filePath)
+ }
+
+ test("Calculate SHA1 Large Sample Image 1") {
+ debug("Starting 'Calculate SHA1 Large Sample Image 1' test")
+ val hash = HashService.getSHA1(LargeSampleImage1)
+ debug(s"Testing that $hash = 4beb6f2d852b75a313863916a1803ebad13a3196")
+ assert(hash == "4beb6f2d852b75a313863916a1803ebad13a3196")
+ }
+
+ test("Calculate SHA1 Medium Sample Image 1") {
+ debug("Starting 'Calculate SHA1 Medium Sample Image 1' test")
+ val hash = HashService.getSHA1(MediumSampleImage1)
+ debug(s"Testing that $hash = edc718ce8e3556a39592ffdc214d0f636529be9f")
+ assert(hash == "edc718ce8e3556a39592ffdc214d0f636529be9f")
+ }
+
+ test("Calculate SHA1 Small Sample Image 1") {
+ debug("Starting 'Calculate SHA1 Small Sample Image 1' test")
+ val hash = HashService.getSHA1(SmallSampleImage1)
+ debug(s"Testing that $hash = 1a91d2b5327f0aad258419f76b87d4c0bc343443")
+ assert(hash == "1a91d2b5327f0aad258419f76b87d4c0bc343443")
+ }
+
+ test("Benchmark SHA1") {
+ info("Benchmarking SHA1")
+ info("SHA1 Large Image 3684x2736")
+ val time = new mutable.MutableList[Long]()
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ sha1TestCase(LargeSampleImage1)
+ }
+ }
+
+ val largeMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
+ time.clear()
+ info("SHA1 Medium Image 1824x1368")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ sha1TestCase(MediumSampleImage1)
+ }
+ }
+
+ val mediumMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
+ time.clear()
+ info("SHA1 Small Image 912x684")
+ for (_ <- 0 until benchmarkRuns) {
+ time += getTime {
+ sha1TestCase(SmallSampleImage1)
+ }
+ }
+
+ val smallMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
+ time.clear()
+ assert(true)
+ }
+}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceTest.scala b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceTest.scala
index c295e59..ceda0e1 100644
--- a/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceTest.scala
+++ b/hash/src/test/scala/com/sothr/imagetools/hash/HashServiceTest.scala
@@ -1,10 +1,12 @@
package com.sothr.imagetools.hash
import java.io.File
+import java.util.NoSuchElementException
import javax.imageio.ImageIO
-import com.sothr.imagetools.hash.`type`.DHash
-import com.sothr.imagetools.hash.dto.{HashSettingDTO, ImageHashDTO}
+import com.sothr.imagetools.hash.HashService.getHash
+import com.sothr.imagetools.hash.`type`.{AHash, DHash, PHash}
+import com.sothr.imagetools.hash.util.ImageUtil
import scala.collection.mutable
@@ -13,453 +15,127 @@ import scala.collection.mutable
*
* Created by dev on 1/23/14.
*/
-class HashServiceTest extends BaseTest {
-
- // Define the number of runs the benchmarking tests should use
- val benchmarkRuns = 10
- val ahashSetting = new HashSettingDTO(true, 8, 8, 0.75f)
- val dhashSetting = new HashSettingDTO(true, 8, 8, 0.85f)
- val phashSetting = new HashSettingDTO(true, 32, 8, 1.0f)
-
- def dhashTestCase(filePath: String): Long = {
- val sample = new File(filePath)
- val image = ImageIO.read(sample)
- HashService.getDhash(dhashSetting, image)
- }
-
- test("Benchmark DHash") {
- info("Benchmarking DHash")
- info("DHash Large Image 3684x2736")
- val time = new mutable.MutableList[Long]()
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- dhashTestCase(TestParams.LargeSampleImage1)
- }
- }
- val largeMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
- time.clear()
- info("DHash Medium Image 1824x1368")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- dhashTestCase(TestParams.MediumSampleImage1)
- }
- }
- val mediumMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
- time.clear()
- info("DHash Small Image 912x684")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- dhashTestCase(TestParams.SmallSampleImage1)
- }
- }
- val smallMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
- time.clear()
- assert(true)
- }
-
- test("Confirm Largest DHash Output ") {
- val testData: Array[Array[Int]] = Array(
- Array(1, 2, 3, 4, 5, 6, 7, 8),
- Array(16, 15, 14, 13, 12, 11, 10, 9),
- Array(17, 18, 19, 20, 21, 22, 23, 24),
- Array(32, 31, 30, 29, 28, 27, 26, 25),
- Array(33, 34, 35, 36, 37, 38, 39, 40),
- Array(48, 47, 46, 45, 44, 43, 42, 41),
- Array(49, 50, 51, 52, 53, 54, 55, 56),
- Array(64, 63, 62, 61, 60, 59, 58, 57))
- val hash = DHash.getHash(testData)
- debug(s"Hash of test array: $hash")
- assert(hash == Long.MaxValue)
- }
-
- test("Calculate DHash Large Sample Image 1") {
- debug("Starting 'Calculate DHash 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.getDhash(dhashSetting, image)
- debug(s"Testing that $hash = 4004374827879799635L")
- assert(hash == 4004374827879799635L)
- }
-
- test("Calculate DHash Medium Sample Image 1") {
- debug("Starting 'Calculate DHash 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.getDhash(dhashSetting, image)
- debug(s"Testing that $hash = 4004374827879799635L")
- assert(hash == 4004374827879799635L)
- }
-
- test("Calculate DHash Small Sample Image 1") {
- debug("Starting 'Calculate DHash 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.getDhash(dhashSetting, image)
- debug(s"Testing that $hash = 4004383623972821843L")
- assert(hash == 4004383623972821843L)
- }
-
- test("DHash Of Large, Medium, And Small Sample 1 Must Be Similar") {
- val largeHash = dhashTestCase(TestParams.LargeSampleImage1)
- val mediumHash = dhashTestCase(TestParams.MediumSampleImage1)
- val smallHash = dhashTestCase(TestParams.SmallSampleImage1)
- assert(HashService.areHashSimilar(dhashSetting, largeHash, mediumHash))
- assert(HashService.areHashSimilar(dhashSetting, largeHash, smallHash))
- assert(HashService.areHashSimilar(dhashSetting, mediumHash, smallHash))
- }
-
- def ahashTestCase(filePath: String): Long = {
- val sample = new File(filePath)
- val image = ImageIO.read(sample)
- HashService.getAhash(ahashSetting, image)
- }
-
- test("Benchmark AHash") {
- info("Benchmarking AHash")
- info("AHash Large Image 3684x2736")
- val time = new mutable.MutableList[Long]()
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- ahashTestCase(TestParams.LargeSampleImage1)
- }
- }
- val largeMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
- time.clear()
- info("AHash Medium Image 1824x1368")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- ahashTestCase(TestParams.MediumSampleImage1)
- }
- }
- val mediumMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
- time.clear()
- info("AHash Small Image 912x684")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- ahashTestCase(TestParams.SmallSampleImage1)
- }
- }
- val smallMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
- time.clear()
- 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(ahashSetting, 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(ahashSetting, 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(ahashSetting, 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.areHashSimilar(ahashSetting, largeHash, mediumHash))
- assert(HashService.areHashSimilar(ahashSetting, largeHash, smallHash))
- assert(HashService.areHashSimilar(ahashSetting, mediumHash, smallHash))
- }
-
- def phashTestCase(filePath: String): Long = {
- val sample = new File(filePath)
- val image = ImageIO.read(sample)
- HashService.getPhash(phashSetting, image)
- }
-
- test("Benchmark PHash") {
- info("Benchmarking PHash")
- info("PHash Large Image 3684x2736")
- val time = new mutable.MutableList[Long]()
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- phashTestCase(TestParams.LargeSampleImage1)
- }
- }
- val largeMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
- time.clear()
- info("PHash Medium Image 1824x1368")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- phashTestCase(TestParams.MediumSampleImage1)
- }
- }
- val mediumMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
- time.clear()
- info("PHash Small Image 912x684")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- phashTestCase(TestParams.SmallSampleImage1)
- }
- }
- val smallMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
- time.clear()
- 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(phashSetting, image)
- debug(s"Testing that $hash = -9154554603604154117L")
- assert(hash == -9154554603604154117L)
- }
-
- 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(phashSetting, 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(phashSetting, 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.areHashSimilar(phashSetting, largeHash, mediumHash))
- assert(HashService.areHashSimilar(phashSetting, largeHash, smallHash))
- assert(HashService.areHashSimilar(phashSetting, mediumHash, smallHash))
- }
-
- def md5TestCase(filePath: String): String = {
- HashService.getMD5(filePath)
- }
-
- test("Benchmark MD5") {
- info("Benchmarking MD5")
- info("MD5 Large Image 3684x2736")
- val time = new mutable.MutableList[Long]()
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- md5TestCase(TestParams.LargeSampleImage1)
- }
- }
- val largeMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
- time.clear()
- info("MD5 Medium Image 1824x1368")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- md5TestCase(TestParams.MediumSampleImage1)
- }
- }
- val mediumMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
- time.clear()
- info("MD5 Small Image 912x684")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- md5TestCase(TestParams.SmallSampleImage1)
- }
- }
- val smallMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
- time.clear()
- assert(true)
- }
-
- test("Calculate MD5 Large Sample Image 1") {
- debug("Starting 'Calculate MD5 Large Sample Image 1' test")
- val hash = HashService.getMD5(TestParams.LargeSampleImage1)
- debug(s"Testing that $hash = 3fbccfd5faf3f991435b827ee5961862")
- assert(hash == "3fbccfd5faf3f991435b827ee5961862")
- }
-
- test("Calculate MD5 Medium Sample Image 1") {
- debug("Starting 'Calculate MD5 Medium Sample Image 1' test")
- val hash = HashService.getMD5(TestParams.MediumSampleImage1)
- debug(s"Testing that $hash = a95e2cc4610307eb957e9c812429c53e")
- assert(hash == "a95e2cc4610307eb957e9c812429c53e")
- }
-
- test("Calculate MD5 Small Sample Image 1") {
- debug("Starting 'Calculate MD5 Small Sample Image 1' test")
- val hash = HashService.getMD5(TestParams.SmallSampleImage1)
- debug(s"Testing that $hash = b137131bd55896c747286e4d247b845e")
- assert(hash == "b137131bd55896c747286e4d247b845e")
- }
-
- def sha1TestCase(filePath: String): String = {
- HashService.getSHA1(filePath)
- }
-
- test("Benchmark SHA1") {
- info("Benchmarking SHA1")
- info("SHA1 Large Image 3684x2736")
- val time = new mutable.MutableList[Long]()
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- sha1TestCase(TestParams.LargeSampleImage1)
- }
- }
- val largeMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
- time.clear()
- info("SHA1 Medium Image 1824x1368")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- sha1TestCase(TestParams.MediumSampleImage1)
- }
- }
- val mediumMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
- time.clear()
- info("SHA1 Small Image 912x684")
- for (_ <- 0 until benchmarkRuns) {
- time += getTime {
- sha1TestCase(TestParams.SmallSampleImage1)
- }
- }
- val smallMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
- time.clear()
- assert(true)
- }
-
- test("Calculate SHA1 Large Sample Image 1") {
- debug("Starting 'Calculate SHA1 Large Sample Image 1' test")
- val hash = HashService.getSHA1(TestParams.LargeSampleImage1)
- debug(s"Testing that $hash = 4beb6f2d852b75a313863916a1803ebad13a3196")
- assert(hash == "4beb6f2d852b75a313863916a1803ebad13a3196")
- }
-
- test("Calculate SHA1 Medium Sample Image 1") {
- debug("Starting 'Calculate SHA1 Medium Sample Image 1' test")
- val hash = HashService.getSHA1(TestParams.MediumSampleImage1)
- debug(s"Testing that $hash = edc718ce8e3556a39592ffdc214d0f636529be9f")
- assert(hash == "edc718ce8e3556a39592ffdc214d0f636529be9f")
- }
-
- test("Calculate SHA1 Small Sample Image 1") {
- debug("Starting 'Calculate SHA1 Small Sample Image 1' test")
- val hash = HashService.getSHA1(TestParams.SmallSampleImage1)
- debug(s"Testing that $hash = 1a91d2b5327f0aad258419f76b87d4c0bc343443")
- assert(hash == "1a91d2b5327f0aad258419f76b87d4c0bc343443")
- }
-
- def imageHashTestCase(filePath: String): ImageHashDTO = {
- HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, filePath)
- }
-
- test("Benchmark getImageHashes") {
- info("Benchmarking getImageHashes")
- info("getImageHashes Large Image 3684x2736")
- val time = new mutable.MutableList[Long]()
- for (runNum <- 0 until benchmarkRuns) {
- time += getTime {
- imageHashTestCase(TestParams.LargeSampleImage1)
- }
- }
- val largeMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
- time.clear()
- info("getImageHashes Medium Image 1824x1368")
- for (runNum <- 0 until benchmarkRuns) {
- time += getTime {
- imageHashTestCase(TestParams.MediumSampleImage1)
- }
- }
- val mediumMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
- time.clear()
- info("getImageHashes Small Image 912x684")
- for (runNum <- 0 until benchmarkRuns) {
- time += getTime {
- imageHashTestCase(TestParams.SmallSampleImage1)
- }
- }
- val smallMean = getMean(time.toArray[Long])
- info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
- time.clear()
- assert(true)
- }
-
- test("ImageHash Of Large, Medium, And Small Sample 1 Must Be Similar") {
- val largeHash = imageHashTestCase(TestParams.LargeSampleImage1)
- val mediumHash = imageHashTestCase(TestParams.MediumSampleImage1)
- val smallHash = imageHashTestCase(TestParams.SmallSampleImage1)
- assert(HashService.areImageHashesSimilar(ahashSetting, dhashSetting, phashSetting, largeHash, mediumHash))
- assert(HashService.areImageHashesSimilar(ahashSetting, dhashSetting, phashSetting, largeHash, smallHash))
- assert(HashService.areImageHashesSimilar(ahashSetting, dhashSetting, phashSetting, mediumHash, smallHash))
- }
-
- test("Calculate ImageHash Large Sample Image 1") {
- debug("Starting 'Calculate ImageHash Large Sample Image 1' test")
- val hash = HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, TestParams.LargeSampleImage1)
- debug(s"Testing that ${hash.hashCode()} = -812844858")
- assert(hash.hashCode == -812844858)
- }
-
- test("Calculate ImageHash Medium Sample Image 1") {
- debug("Starting 'Calculate ImageHash Medium Sample Image 1' test")
- val hash = HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, TestParams.MediumSampleImage1)
- debug(s"Testing that ${hash.hashCode()} = -812836666")
- assert(hash.hashCode == -812836666)
- }
-
- test("Calculate ImageHash Small Sample Image 1") {
- debug("Starting 'Calculate ImageHash Small Sample Image 1' test")
- val hash = HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, TestParams.SmallSampleImage1)
- debug(s"Testing that ${hash.hashCode()} = -812840762")
- assert(hash.hashCode == -812840762)
- }
-
-}
+class HashServiceTest extends HashServiceBaseTest {
+
+ def imageHashTestCase(filePath: String): ImageHash = {
+ HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, filePath)
+ }
+
+ test("ImageHash Of Large, Medium, And Small Sample 1 Must Be Similar") {
+ val largeHash = imageHashTestCase(LargeSampleImage1)
+ val mediumHash = imageHashTestCase(MediumSampleImage1)
+ val smallHash = imageHashTestCase(SmallSampleImage1)
+ assert(HashService.areImageHashesSimilar(ahashSetting, dhashSetting, phashSetting, largeHash, mediumHash))
+ assert(HashService.areImageHashesSimilar(ahashSetting, dhashSetting, phashSetting, largeHash, smallHash))
+ assert(HashService.areImageHashesSimilar(ahashSetting, dhashSetting, phashSetting, mediumHash, smallHash))
+ }
+
+ test("Calculate ImageHash Large Sample Image 1") {
+ debug("Starting 'Calculate ImageHash Large Sample Image 1' test")
+ val hash = HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, LargeSampleImage1)
+ debug(s"Testing that ${hash.hashCode()} = -812844858")
+ assert(hash.hashCode == -812844858)
+ }
+
+ test("Calculate ImageHash Medium Sample Image 1") {
+ debug("Starting 'Calculate ImageHash Medium Sample Image 1' test")
+ val hash = HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, MediumSampleImage1)
+ debug(s"Testing that ${hash.hashCode()} = -812836666")
+ assert(hash.hashCode == -812836666)
+ }
+
+ test("Calculate ImageHash Small Sample Image 1") {
+ debug("Starting 'Calculate ImageHash Small Sample Image 1' test")
+ val hash = HashService.getImageHashes(ahashSetting, dhashSetting, phashSetting, SmallSampleImage1)
+ debug(s"Testing that ${hash.hashCode()} = -812840762")
+ assert(hash.hashCode == -812840762)
+ }
+
+ test("Benchmark getImageHashes") {
+ info("Benchmarking getImageHashes")
+ info("getImageHashes Large Image 3684x2736")
+ val time = new mutable.MutableList[Long]()
+ for (runNum <- 0 until benchmarkRuns) {
+ time += getTime {
+ imageHashTestCase(LargeSampleImage1)
+ }
+ }
+
+ val largeMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for large was: $largeMean ms")
+ time.clear()
+ info("getImageHashes Medium Image 1824x1368")
+ for (runNum <- 0 until benchmarkRuns) {
+ time += getTime {
+ imageHashTestCase(MediumSampleImage1)
+ }
+ }
+
+ val mediumMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for medium was: $mediumMean ms")
+ time.clear()
+ info("getImageHashes Small Image 912x684")
+ for (runNum <- 0 until benchmarkRuns) {
+ time += getTime {
+ imageHashTestCase(SmallSampleImage1)
+ }
+ }
+
+ val smallMean = getMean(time.toArray[Long])
+ info(s"The mean time of ${time.size} tests for small was: $smallMean ms")
+ time.clear()
+ assert(true)
+ }
+
+ test("GetPrecisionSet - Empty Precision Set") {
+ val emptyPrecisionSet = HashService.getPrecisionSet(
+ new HashSetting("Test1", false, 8, 0, 0),
+ new HashSetting("Test2", false, 16, 0, 0),
+ new HashSetting("Test3", false, 32, 0, 0))
+ assert(emptyPrecisionSet.isEmpty)
+ }
+
+ test("GetPrecisionSet - Overlapping Precision Set") {
+ val precisionSet = HashService.getPrecisionSet(
+ new HashSetting("Test1", true, 8, 0, 0),
+ new HashSetting("Test2", true, 8, 0, 0),
+ new HashSetting("Test3", true, 16, 0, 0))
+ assert(precisionSet.nonEmpty)
+ assert(2 == precisionSet.size)
+ }
+
+ test("GetPrecisionMap - Empty Precision Set") {
+ val emptyPrecisionSet = HashService.getPrecisionSet(
+ new HashSetting("Test1", false, 8, 0, 0),
+ new HashSetting("Test2", false, 16, 0, 0),
+ new HashSetting("Test3", false, 32, 0, 0))
+ val grayImage = ImageUtil.convertToGray(ImageIO.read(new File(SmallSampleImage1)))
+ val emptyPrecisionMap = HashService.getPrecisionMap(emptyPrecisionSet, grayImage)
+ assert(emptyPrecisionMap.isEmpty)
+ }
+
+ test("GetPrecisionMap - Overlapping Precision Set") {
+ val precisionSet = HashService.getPrecisionSet(
+ new HashSetting("Test1", true, 8, 0, 0),
+ new HashSetting("Test2", true, 8, 0, 0),
+ new HashSetting("Test3", true, 16, 0, 0))
+ val grayImage = ImageUtil.convertToGray(ImageIO.read(new File(SmallSampleImage1)))
+ val precisionMap = HashService.getPrecisionMap(precisionSet, grayImage)
+ assert(precisionMap.nonEmpty)
+ assert(precisionMap.size == 2)
+ }
+
+ test("getHash - Empty Precision Set") {
+ val emptyPrecisionSet = HashService.getPrecisionSet(
+ new HashSetting("Test1", false, 8, 0, 0),
+ new HashSetting("Test2", false, 16, 0, 0),
+ new HashSetting("Test3", false, 32, 0, 0))
+ val grayImage = ImageUtil.convertToGray(ImageIO.read(new File(SmallSampleImage1)))
+ val emptyPrecisionMap = HashService.getPrecisionMap(emptyPrecisionSet, grayImage)
+ assertThrows[NoSuchElementException] {
+ val ahash: Long = getHash(ahashSetting, emptyPrecisionMap(ahashSetting.precision), AHash.getHash)
+ val dhash: Long = getHash(dhashSetting, emptyPrecisionMap(dhashSetting.precision), DHash.getHash)
+ val phash: Long = getHash(phashSetting, emptyPrecisionMap(phashSetting.precision), PHash.getHash)
+ }
+ }
+}
\ No newline at end of file
diff --git a/hash/src/test/scala/com/sothr/imagetools/hash/TestParams.scala b/hash/src/test/scala/com/sothr/imagetools/hash/TestParams.scala
deleted file mode 100644
index 1d79b1c..0000000
--- a/hash/src/test/scala/com/sothr/imagetools/hash/TestParams.scala
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.sothr.imagetools.hash
-
-object TestParams {
- val LargeSampleImage1 = "sample/sample_01_large.jpg"
- val MediumSampleImage1 = "sample/sample_01_medium.jpg"
- val SmallSampleImage1 = "sample/sample_01_small.jpg"
-}
\ No newline at end of file
diff --git a/parent/pom.xml b/parent/pom.xml
index 373fa5e..48a4e25 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -70,8 +70,10 @@
- 0.1.1
+
+ 0.2.0
0.1.3
+
UTF-8
1.8
2.12