Skip to content

Commit

Permalink
Merge pull request #8 from poslegm/api-refactoring
Browse files Browse the repository at this point in the history
Package name simplified
  • Loading branch information
poslegm authored Nov 11, 2018
2 parents f716362 + e13eb05 commit 08373d9
Show file tree
Hide file tree
Showing 11 changed files with 44 additions and 47 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"))
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.poslegm.scalaphash
package scalaphash

private[scalaphash] object MathUtils {
type FloatMatrix = Array[Array[Float]]
Expand Down
Original file line number Diff line number Diff line change
@@ -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]
Expand All @@ -13,21 +15,23 @@ 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
* (http://www.phash.org/docs/pubs/thesis_zauner.pdf / page 21)
* @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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.poslegm.scalaphash
package scalaphash

import java.awt.image._
import java.awt.RenderingHints
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.poslegm.scalaphash
package scalaphash

/**
* Utilitary class for projections computing (Radial Hash algorithm)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.poslegm.scalaphash
package scalaphash

import java.io.File
import javax.imageio.ImageIO
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.poslegm.scalaphash
package scalaphash

import java.io.File
import javax.imageio.ImageIO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.poslegm.scalaphash
package scalaphash

import java.io.File
import javax.imageio.ImageIO
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.poslegm.scalaphash
package scalaphash

import java.io.File
import javax.imageio.ImageIO
Expand Down

0 comments on commit 08373d9

Please sign in to comment.