diff --git a/README.md b/README.md index 6e005e7..6368c36 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ My library implements three Perceptual Hashing algorithms: Radial Hash, DCT hash ```scala resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" -libraryDependencies += "com.github.poslegm" %% "scala-phash" % "1.1.1" +libraryDependencies += "com.github.poslegm" %% "scala-phash" % "1.2.0" ``` #### API @@ -25,12 +25,12 @@ There is three functions for each hashing algorithm. Let's consider them by exam Similar functions written for Marr and Radial Hash algorithms. -All public api with scaladocs decsribed in trait [`PHashAlgebra`](https://github.com/poslegm/scala-phash/blob/master/src/main/scala/com/github/poslegm/scalaphash/PHashAlgebra.scala). +All public api with scaladocs decsribed in object [`PHash`](https://github.com/poslegm/scala-phash/blob/master/src/main/scala/com/github/poslegm/scalaphash/PHash.scala). #### Example ```scala -import com.github.poslegm.scalaphash.PHash._ +import scalaphash.PHash._ import javax.imageio.ImageIO val img1 = ImageIO.read(new File("img1.jpg")) diff --git a/build.sbt b/build.sbt index 0777640..4c58ff5 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ name := "scala-phash" organization := "com.github.poslegm" -version := "1.1.1" +version := "1.2.0" scalaVersion := "2.12.6" crossScalaVersions := Seq("2.11.8", "2.12.1", scalaVersion.value) diff --git a/src/main/scala/com/github/poslegm/scalaphash/MathUtils.scala b/src/main/scala/scalaphash/MathUtils.scala similarity index 96% rename from src/main/scala/com/github/poslegm/scalaphash/MathUtils.scala rename to src/main/scala/scalaphash/MathUtils.scala index 2afb872..e041783 100644 --- a/src/main/scala/com/github/poslegm/scalaphash/MathUtils.scala +++ b/src/main/scala/scalaphash/MathUtils.scala @@ -1,4 +1,4 @@ -package com.github.poslegm.scalaphash +package scalaphash private[scalaphash] object MathUtils { type FloatMatrix = Array[Array[Float]] diff --git a/src/main/scala/com/github/poslegm/scalaphash/PHashAlgebra.scala b/src/main/scala/scalaphash/PHash.scala similarity index 70% rename from src/main/scala/com/github/poslegm/scalaphash/PHashAlgebra.scala rename to src/main/scala/scalaphash/PHash.scala index 6e066ba..3881388 100644 --- a/src/main/scala/com/github/poslegm/scalaphash/PHashAlgebra.scala +++ b/src/main/scala/scalaphash/PHash.scala @@ -1,8 +1,10 @@ -package com.github.poslegm.scalaphash +package scalaphash import java.awt.image.BufferedImage -trait PHashAlgebra { +import scala.util.control.NonFatal + +object PHash { type DCTHash = Long type MarrHash = Array[Int] type RadialHash = Array[Int] @@ -13,7 +15,9 @@ trait PHashAlgebra { * @param image image for hashing * @return 64-bit hash value or exception * */ - def dctHash(image: BufferedImage): Either[Throwable, DCTHash] + def dctHash(image: BufferedImage): Either[Throwable, DCTHash] = + try Right(unsafeDctHash(image)) + catch { case NonFatal(e) => Left(e) } /** * Computes DCT hash value of image @@ -21,13 +25,13 @@ trait PHashAlgebra { * @param image image for hashing * @return 64-bit hash value * */ - def unsafeDctHash(image: BufferedImage): DCTHash + def unsafeDctHash(image: BufferedImage): DCTHash = PHashInternal.unsafeDctHash(image) /** * Computes distance between two DCT hashes * Less is better * */ - def dctHashDistance(hash1: DCTHash, hash2: DCTHash): Long + def dctHashDistance(hash1: DCTHash, hash2: DCTHash): Long = PHashInternal.dctHashDistance(hash1, hash2) /** * Computes Marr hash value of image @@ -37,7 +41,9 @@ trait PHashAlgebra { * @param level coefficient for correlation kernel * @return hash as int array or exception * */ - def marrHash(image: BufferedImage, alpha: Int = 2, level: Int = 1): Either[Throwable, MarrHash] + def marrHash(image: BufferedImage, alpha: Int = 2, level: Int = 1): Either[Throwable, MarrHash] = + try Right(unsafeMarrHash(image, alpha, level)) + catch { case NonFatal(e) => Left(e) } /** * Computes Marr hash value of image @@ -47,13 +53,14 @@ trait PHashAlgebra { * @param level coefficient for correlation kernel * @return hash as int array * */ - def unsafeMarrHash(image: BufferedImage, alpha: Int = 2, level: Int = 1): MarrHash + def unsafeMarrHash(image: BufferedImage, alpha: Int = 2, level: Int = 1): MarrHash = + PHashInternal.unsafeMarrHash(image, alpha, level) /** * Computes distance between two Marr hashes * Less is better * */ - def marrHashDistance(hash1: MarrHash, hash2: MarrHash): Option[Double] + def marrHashDistance(hash1: MarrHash, hash2: MarrHash): Option[Double] = PHashInternal.marrHashDistance(hash1, hash2) /** * Computes Radial hash value of image @@ -62,7 +69,9 @@ trait PHashAlgebra { * @param projectionsCount number of projections to compute * @return hash as int array or exception * */ - def radialHash(image: BufferedImage, projectionsCount: Int = 180): Either[Throwable, RadialHash] + def radialHash(image: BufferedImage, projectionsCount: Int = 180): Either[Throwable, RadialHash] = + try Right(unsafeRadialHash(image, projectionsCount)) + catch { case NonFatal(e) => Left(e) } /** * Computes Radial hash value of image @@ -71,11 +80,12 @@ trait PHashAlgebra { * @param projectionsCount number of projections to compute * @return hash as int array * */ - def unsafeRadialHash(image: BufferedImage, projectionsCount: Int = 180): RadialHash + def unsafeRadialHash(image: BufferedImage, projectionsCount: Int = 180): RadialHash = + PHashInternal.unsafeRadialHash(image, projectionsCount) /** * Computes distance between two Radial hashes * More is better * */ - def radialHashDistance(hash1: RadialHash, hash2: RadialHash): Double + def radialHashDistance(hash1: RadialHash, hash2: RadialHash): Double = PHashInternal.radialHashDistance(hash1, hash2) } diff --git a/src/main/scala/com/github/poslegm/scalaphash/PHash.scala b/src/main/scala/scalaphash/PhashInternal.scala similarity index 83% rename from src/main/scala/com/github/poslegm/scalaphash/PHash.scala rename to src/main/scala/scalaphash/PhashInternal.scala index fc8fad2..3cf8761 100644 --- a/src/main/scala/com/github/poslegm/scalaphash/PHash.scala +++ b/src/main/scala/scalaphash/PhashInternal.scala @@ -1,22 +1,18 @@ -package com.github.poslegm.scalaphash +package scalaphash import java.awt.image.BufferedImage -import com.github.poslegm.scalaphash.MathUtils._ +import scalaphash.MathUtils._ +import scalaphash.PHash._ import scala.annotation.tailrec import scala.collection.mutable.ArrayBuffer -import scala.util.control.NonFatal -object PHash extends PHashAlgebra { +private[scalaphash] object PHashInternal { private lazy val dctMatrix = createDctMatrix(32) private lazy val dctMatrixTransposed = dctMatrix.transpose - override def dctHash(image: BufferedImage): Either[Throwable, DCTHash] = - try Right(unsafeDctHash(image)) - catch { case NonFatal(e) => Left(e) } - - override def unsafeDctHash(image: BufferedImage): DCTHash = { + def unsafeDctHash(image: BufferedImage): DCTHash = { val processedImage = PixelMatrix(image).makeGrayScale().makeConvolved() val matrix = processedImage.resize(32, 32).toMatrix val dctImage = dctMatrix * matrix * dctMatrixTransposed @@ -29,7 +25,7 @@ object PHash extends PHashAlgebra { } } - override def dctHashDistance(hash1: DCTHash, hash2: DCTHash): Long = { + def dctHashDistance(hash1: DCTHash, hash2: DCTHash): Long = { var x = hash1 ^ hash2 val m1 = 0x5555555555555555L val m2 = 0x3333333333333333L @@ -41,12 +37,7 @@ object PHash extends PHashAlgebra { (x * h01) >> 56 } - override def marrHash(image: BufferedImage, alpha: Int = 2, level: Int = 1): Either[Throwable, MarrHash] = - try Right(unsafeMarrHash(image, alpha, level)) - catch { case NonFatal(e) => Left(e) } - - override def unsafeMarrHash(image: BufferedImage, alpha: Int = 2, level: Int = 1): MarrHash = { - + def unsafeMarrHash(image: BufferedImage, alpha: Int, level: Int): MarrHash = { val processed = PixelMatrix(image).makeGrayScale().makeBlurred().resize(512, 512).equalize(256) val kernel = createMarrKernel(alpha, level) @@ -88,7 +79,7 @@ object PHash extends PHashAlgebra { hash.toArray } - override def marrHashDistance(hash1: MarrHash, hash2: MarrHash): Option[Double] = + def marrHashDistance(hash1: MarrHash, hash2: MarrHash): Option[Double] = if (hash1.length != hash2.length || hash1.isEmpty) { None } else { @@ -99,11 +90,7 @@ object PHash extends PHashAlgebra { Some(distance / maxBitsCount) } - override def radialHash(image: BufferedImage, projectionsCount: Int = 180): Either[Throwable, RadialHash] = - try Right(unsafeRadialHash(image, projectionsCount)) - catch { case NonFatal(e) => Left(e) } - - override def unsafeRadialHash(image: BufferedImage, projectionsCount: Int = 180): RadialHash = { + def unsafeRadialHash(image: BufferedImage, projectionsCount: Int): RadialHash = { val grayscaled = if (image.getColorModel.getColorSpace.getNumComponents >= 3) { PixelMatrix(image).makeGrayScale() } else { @@ -116,7 +103,7 @@ object PHash extends PHashAlgebra { calculateRadialHash(features) } - override def radialHashDistance(hash1: RadialHash, hash2: RadialHash): Double = { + def radialHashDistance(hash1: RadialHash, hash2: RadialHash): Double = { val meanX: Double = hash1.sum / hash2.length val meanY: Double = hash2.sum / hash2.length var max = 0.0 diff --git a/src/main/scala/com/github/poslegm/scalaphash/PixelMatrix.scala b/src/main/scala/scalaphash/PixelMatrix.scala similarity index 99% rename from src/main/scala/com/github/poslegm/scalaphash/PixelMatrix.scala rename to src/main/scala/scalaphash/PixelMatrix.scala index 94a9b3a..ae402d5 100644 --- a/src/main/scala/com/github/poslegm/scalaphash/PixelMatrix.scala +++ b/src/main/scala/scalaphash/PixelMatrix.scala @@ -1,4 +1,4 @@ -package com.github.poslegm.scalaphash +package scalaphash import java.awt.image._ import java.awt.RenderingHints diff --git a/src/main/scala/com/github/poslegm/scalaphash/RadialProjections.scala b/src/main/scala/scalaphash/RadialProjections.scala similarity index 98% rename from src/main/scala/com/github/poslegm/scalaphash/RadialProjections.scala rename to src/main/scala/scalaphash/RadialProjections.scala index 7c3edbd..10f9fe4 100644 --- a/src/main/scala/com/github/poslegm/scalaphash/RadialProjections.scala +++ b/src/main/scala/scalaphash/RadialProjections.scala @@ -1,4 +1,4 @@ -package com.github.poslegm.scalaphash +package scalaphash /** * Utilitary class for projections computing (Radial Hash algorithm) diff --git a/src/test/scala/com/github/poslegm/scalaphash/DCTHashTest.scala b/src/test/scala/com/github/poslegm/scalaphash/DCTHashTest.scala index 2d7d7bc..36c3d74 100644 --- a/src/test/scala/com/github/poslegm/scalaphash/DCTHashTest.scala +++ b/src/test/scala/com/github/poslegm/scalaphash/DCTHashTest.scala @@ -1,4 +1,4 @@ -package com.github.poslegm.scalaphash +package scalaphash import java.io.File import javax.imageio.ImageIO @@ -18,7 +18,7 @@ class DCTHashTest extends FlatSpec with Matchers with PrivateMethodTester { ).map(_.map(_.toFloat)) val createDctMatrix = PrivateMethod[Array[Array[Float]]]('createDctMatrix) - val matrix = PHash invokePrivate createDctMatrix(6) + val matrix = PHashInternal invokePrivate createDctMatrix(6) matrix.length shouldEqual canonical.length matrix(0).length shouldEqual canonical(0).length diff --git a/src/test/scala/com/github/poslegm/scalaphash/ImageUtilsTest.scala b/src/test/scala/com/github/poslegm/scalaphash/ImageUtilsTest.scala index 43b237e..29ce37b 100644 --- a/src/test/scala/com/github/poslegm/scalaphash/ImageUtilsTest.scala +++ b/src/test/scala/com/github/poslegm/scalaphash/ImageUtilsTest.scala @@ -1,4 +1,4 @@ -package com.github.poslegm.scalaphash +package scalaphash import java.io.File import javax.imageio.ImageIO diff --git a/src/test/scala/com/github/poslegm/scalaphash/MarrHashTest.scala b/src/test/scala/com/github/poslegm/scalaphash/MarrHashTest.scala index c5c0070..70b2870 100644 --- a/src/test/scala/com/github/poslegm/scalaphash/MarrHashTest.scala +++ b/src/test/scala/com/github/poslegm/scalaphash/MarrHashTest.scala @@ -1,4 +1,4 @@ -package com.github.poslegm.scalaphash +package scalaphash import java.io.File import javax.imageio.ImageIO @@ -25,7 +25,7 @@ class MarrHashTest extends FlatSpec with Matchers with PrivateMethodTester { ).map(_.map(_.toFloat)) val createMarrKernel = PrivateMethod[Array[Array[Float]]]('createMarrKernel) - val matrix = PHash invokePrivate createMarrKernel(1, 1) + val matrix = PHashInternal invokePrivate createMarrKernel(1, 1) matrix.length shouldEqual canonical.length matrix.indices.foreach(i => matrix(i).length shouldEqual canonical(i).length) diff --git a/src/test/scala/com/github/poslegm/scalaphash/RadialHashTest.scala b/src/test/scala/com/github/poslegm/scalaphash/RadialHashTest.scala index 7bd2ee9..919a0e5 100644 --- a/src/test/scala/com/github/poslegm/scalaphash/RadialHashTest.scala +++ b/src/test/scala/com/github/poslegm/scalaphash/RadialHashTest.scala @@ -1,4 +1,4 @@ -package com.github.poslegm.scalaphash +package scalaphash import java.io.File import javax.imageio.ImageIO