Browse Source
Started work on hashing and added a sample image for testing. First new unit test is broken due to array indexing issues. Configured the pom further for testing configuration. Added a few properties to support different levels or precision depending on the hash.
master
Started work on hashing and added a sample image for testing. First new unit test is broken due to array indexing issues. Configured the pom further for testing configuration. Added a few properties to support different levels or precision depending on the hash.
master
Drew Short
11 years ago
15 changed files with 250 additions and 18 deletions
-
3.gitignore
-
18pom.xml
-
BINsrc/includes/sample/sample_01_large.jpg
-
4src/main/resources/default.properties
-
10src/main/scala/com/sothr/imagetools/hash/AHash.scala
-
30src/main/scala/com/sothr/imagetools/hash/DHash.scala
-
36src/main/scala/com/sothr/imagetools/hash/HashService.scala
-
10src/main/scala/com/sothr/imagetools/hash/PHash.scala
-
10src/main/scala/com/sothr/imagetools/hash/PerceptualHasher.scala
-
81src/main/scala/com/sothr/imagetools/image/ImageService.scala
-
4src/main/scala/com/sothr/imagetools/util/PropertiesEnum.scala
-
3src/test/java/com/sothr/imagetools/AppTest.java
-
25src/test/resources/default.properties
-
11src/test/scala/com/sothr/imagetools/BaseTest.scala
-
19src/test/scala/com/sothr/imagetools/hash/HashServiceTest.scala
After Width: 3648 | Height: 2736 | Size: 5.0 MiB |
@ -0,0 +1,10 @@ |
|||||
|
package com.sothr.imagetools.hash |
||||
|
|
||||
|
/** |
||||
|
* Created by dev on 1/22/14. |
||||
|
*/ |
||||
|
object AHash extends PerceptualHasher { |
||||
|
def getHash(imageData: Array[Array[Int]]): Long = { |
||||
|
return 0L |
||||
|
} |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
package com.sothr.imagetools.hash |
||||
|
|
||||
|
/** |
||||
|
* Created by dev on 1/22/14. |
||||
|
*/ |
||||
|
object DHash extends PerceptualHasher { |
||||
|
def getHash(imageData: Array[Array[Int]]): Long = { |
||||
|
val width = imageData.length |
||||
|
val height = imageData(0).length |
||||
|
var hash = 0L |
||||
|
for (row <- 0 until width) { |
||||
|
//println(f"Row: $row%d") |
||||
|
var previousPixel = imageData(row)(0) |
||||
|
var previousLocation = (row, 0) |
||||
|
|
||||
|
//process each column |
||||
|
for (col <- 0 until height) { |
||||
|
//println(f"Column: $col%d") |
||||
|
hash <<= 1 |
||||
|
val pixel = imageData(row)(col) |
||||
|
//binary or 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 |
||||
|
previousPixel = pixel |
||||
|
previousLocation = (row, col) |
||||
|
} |
||||
|
} |
||||
|
hash |
||||
|
} |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
package com.sothr.imagetools.hash |
||||
|
|
||||
|
/** |
||||
|
* Created by dev on 1/22/14. |
||||
|
*/ |
||||
|
object PHash extends PerceptualHasher { |
||||
|
def getHash(imageData: Array[Array[Int]]): Long = { |
||||
|
return 0L |
||||
|
} |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
package com.sothr.imagetools.hash |
||||
|
|
||||
|
/** |
||||
|
* Created by dev on 1/22/14. |
||||
|
*/ |
||||
|
trait PerceptualHasher { |
||||
|
|
||||
|
def getHash(imageData:Array[Array[Int]]):Long |
||||
|
|
||||
|
} |
@ -1,14 +1,91 @@ |
|||||
package com.sothr.imagetools |
package com.sothr.imagetools |
||||
|
|
||||
import grizzled.slf4j.Logging |
import grizzled.slf4j.Logging |
||||
|
import java.awt.image.{DataBufferByte, BufferedImage} |
||||
|
import net.coobird.thumbnailator.Thumbnails |
||||
|
|
||||
object ImageService extends Logging { |
object ImageService extends Logging { |
||||
|
|
||||
/** |
/** |
||||
* Get the raw data for an image |
* Get the raw data for an image |
||||
*/ |
*/ |
||||
def getImageData(path:String):Array[Array[Int]] = { |
|
||||
return null |
|
||||
|
def getImageData(image:BufferedImage):Array[Array[Int]] = { |
||||
|
return convertTo2DWithoutUsingGetRGB(image) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Quickly convert an image to grayscale |
||||
|
* |
||||
|
* @param image |
||||
|
* @return |
||||
|
*/ |
||||
|
def convertToGray(image:BufferedImage):BufferedImage = { |
||||
|
val grayImage = new BufferedImage(image.getWidth, image.getHeight, BufferedImage.TYPE_BYTE_GRAY) |
||||
|
val g = image.getGraphics |
||||
|
g.drawImage(image,0,0,null) |
||||
|
g.dispose() |
||||
|
grayImage |
||||
|
} |
||||
|
|
||||
|
def resize(image:BufferedImage, size:Int, forced:Boolean=false):BufferedImage = { |
||||
|
if (forced) { |
||||
|
Thumbnails.of(image).forceSize(size,size).asBufferedImage |
||||
|
} else { |
||||
|
Thumbnails.of(image).size(size,size).asBufferedImage |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Convert a buffered image into a 2d pixel data array |
||||
|
* |
||||
|
* @param image |
||||
|
* @return |
||||
|
*/ |
||||
|
private def convertTo2DWithoutUsingGetRGB(image:BufferedImage):Array[Array[Int]] = { |
||||
|
|
||||
|
val pixels = image.getRaster.getDataBuffer.asInstanceOf[DataBufferByte].getData |
||||
|
val width = image.getWidth |
||||
|
val height = image.getHeight |
||||
|
val hasAlphaChannel = image.getAlphaRaster != null |
||||
|
|
||||
|
val result = Array.ofDim[Int](height,width) |
||||
|
if (hasAlphaChannel) { |
||||
|
val pixelLength = 4 |
||||
|
var row = 0 |
||||
|
var col = 0 |
||||
|
for (pixel <- 0 until pixels.length by pixelLength) { |
||||
|
var argb:Int = 0 |
||||
|
argb += (pixels(pixel) & 0xff) << 24 //alpha |
||||
|
argb += (pixels(pixel + 1) & 0xff) //blue |
||||
|
argb += (pixels(pixel + 2) & 0xff) << 8 //green |
||||
|
argb += (pixels(pixel + 3) & 0xff) << 16 //red |
||||
|
result(row)(col) = argb |
||||
|
col += 1 |
||||
|
if (col == width) { |
||||
|
col = 0 |
||||
|
row += 1 |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
val pixelLength = 3 |
||||
|
var row = 0 |
||||
|
var col = 0 |
||||
|
for (pixel <- 0 until pixels.length by pixelLength) { |
||||
|
var argb:Int = 0 |
||||
|
argb += -16777216; // 255 alpha |
||||
|
argb += (pixels(pixel) & 0xff) //blue |
||||
|
argb += (pixels(pixel + 1) & 0xff) << 8 //green |
||||
|
argb += (pixels(pixel + 2) & 0xff) << 16 //red |
||||
|
result(row)(col) = argb |
||||
|
col += 1 |
||||
|
if (col == width) { |
||||
|
col = 0 |
||||
|
row += 1 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result |
||||
} |
} |
||||
|
|
||||
} |
} |
@ -0,0 +1,25 @@ |
|||||
|
#Default Properties File |
||||
|
#Image Tools version: ${project.version} |
||||
|
version=${project.version} |
||||
|
|
||||
|
#Default Image Settings |
||||
|
#images must be 90% similar |
||||
|
image.differenceThreshold=0.90 |
||||
|
#control generation of hashes for new images. |
||||
|
image.hash.precision=64 |
||||
|
image.ahash.use=true |
||||
|
image.ahash.weight=0.70 |
||||
|
image.ahash.precision=8 |
||||
|
image.dhash.use=true |
||||
|
image.dhash.weight=0.85 |
||||
|
image.dhash.precision=8 |
||||
|
#set to false if hashing images is taking too long |
||||
|
image.phash.use=true |
||||
|
image.phash.weight=1.0 |
||||
|
image.phash.precision=16 |
||||
|
|
||||
|
#Default Thumbnail Settings |
||||
|
#Directory where to store thumbnails |
||||
|
thumbnail.directory=./cache/thumbnails/ |
||||
|
#Size of the thumbnail to generate and store |
||||
|
thumbnail.size=128 |
@ -1,5 +1,12 @@ |
|||||
package com.sothr.imagetools |
package com.sothr.imagetools |
||||
|
|
||||
import org.scalatest.{FunSuite,Matchers,OptionValues,Inside,Inspectors} |
|
||||
|
import org.scalatest.{FunSuite,Matchers,OptionValues,Inside,Inspectors,BeforeAndAfter} |
||||
|
|
||||
abstract class BaseTest extends FunSuite with Matchers with OptionValues with Inside with Inspectors |
|
||||
|
abstract class BaseTest extends FunSuite with Matchers with OptionValues with Inside with Inspectors with BeforeAndAfter { |
||||
|
|
||||
|
before { |
||||
|
AppConfig.configLogging() |
||||
|
AppConfig.loadProperties() |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package com.sothr.imagetools.hash |
||||
|
|
||||
|
import com.sothr.imagetools.BaseTest |
||||
|
import javax.imageio.ImageIO |
||||
|
import java.io.File |
||||
|
|
||||
|
/** |
||||
|
* Created by dev on 1/23/14. |
||||
|
*/ |
||||
|
class HashServiceTest extends BaseTest { |
||||
|
|
||||
|
test("Calculate DHash") { |
||||
|
val sample = new File("./target/sample/sample_01_large.jpg") |
||||
|
val image = ImageIO.read(sample) |
||||
|
val hash = HashService.getDhash(image) |
||||
|
assert(hash == 0L) |
||||
|
} |
||||
|
|
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue