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 |
|||
|
|||
import grizzled.slf4j.Logging |
|||
import java.awt.image.{DataBufferByte, BufferedImage} |
|||
import net.coobird.thumbnailator.Thumbnails |
|||
|
|||
object ImageService extends Logging { |
|||
|
|||
/** |
|||
* 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 |
|||
|
|||
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