Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ShaharMS committed Oct 25, 2024
1 parent d2f04c5 commit 11477b7
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 3 deletions.
27 changes: 25 additions & 2 deletions src/vision/Vision.hx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package vision;

import haxe.io.Bytes;
import haxe.crypto.Sha256;
import vision.exceptions.Unimplemented;
import vision.ds.specifics.SimilarityScoringMechanism;
import vision.algorithms.KMeans;
Expand Down Expand Up @@ -1480,13 +1482,34 @@ class Vision {
}

public static function kmeansGroupSimilarImages(images:Array<Image>, groupCount:Int = 16):Array<Array<Image>> {
var clusterMap:Map<Image, Array<Color>> = new Map<Image, Array<Color>>();
var clusterMap:Map<Bytes, Array<Color>> = new Map();
for (image in images) {
var kmeans = Vision.kmeansGroupImageColors(image, groupCount);
var clusterCenters = kmeans.map((array) -> Color.getAverage(array, false));
clusterMap.set(image, clusterCenters);

var hash = Sha256.make(image);
if (!clusterMap.exists(hash))
clusterMap.set(hash, clusterCenters);
}

var kmeansOfImages = KMeans.generateClustersUsingConvergence(
[for (key in clusterMap.keys()) key],
groupCount,
(ha, hb) -> {
var a = clusterMap.get(ha);
var b = clusterMap.get(hb);
return a.distanceTo(b, (c1, c2) -> Color.distanceBetween(c1, c2, false));
},
(hashArrays) -> {
var arrays = hashArrays.map((hash) -> clusterMap.get(hash));
var average = [];
for (i in 0...arrays[0].length) {
average.push(Color.getAverage([for (array in arrays) array[i]], false));
}
return average;
}
);

throw new Unimplemented("kmeansGroupSimilarImages");
}

Expand Down
56 changes: 55 additions & 1 deletion src/vision/algorithms/ImageHashing.hx
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package vision.algorithms;

import haxe.Int64;
import vision.ds.Matrix2D;
import vision.tools.ImageTools;
import vision.ds.ByteArray;
import vision.ds.Image;
import vision.ds.ImageResizeAlgorithm;

using vision.tools.MathTools;

/**
An implementation of the following image hashing algorithms:
- Average Hashing (`ahash`)
- Preceptual Hashing (`phash`)
**/
class ImageHashing {

public static function ahash(image:Image, hashByteSize:Int = 4):ByteArray {
public static function ahash(image:Image, hashByteSize:Int = 16):ByteArray {
var clone = image.clone();
var length = Math.floor(Math.sqrt(hashByteSize));

Expand All @@ -34,4 +39,53 @@ class ImageHashing {
return bytes;
}

public static function phash(image:Image):ByteArray {
var clone = image.clone();

clone.resize(32, 32);

// Grayscale
for (x in 0...32) {
for (y in 0...32) {
var color = clone.getUnsafePixel(x, y);
clone.setUnsafePixel(x, y, color.grayscale());
}
}

var dctMatrix = new Matrix2D(32, 32);

var ci, cj, dct1, sum;

for (i in 0...32) {
for (j in 0...32) {
ci = i == 0 ? 1 / 32.sqrt() : 2.sqrt() / 32.sqrt();
cj = j == 0 ? 1 / 32.sqrt() : 2.sqrt() / 32.sqrt();
sum = 0.;

for (k in 0...32) {
for (l in 0...32) {
dct1 = clone.getUnsafePixel(k, l).red *
Math.cos((2 * k + 1) * i * Math.PI / (2 * 32)) *
Math.cos((2 * l + 1) * j * Math.PI / (2 * 32));
sum += dct1;
}
}

dctMatrix.set(i, j, ci * cj * sum);
}
}

var submatrix = dctMatrix.getSubMatrix(0, 0, 8, 8);
var average = submatrix.getAverage();

// Construct the hash - each cell with its value above the
// Average is assigned 1, whiel below is 0.
var hash = Int64.make(0, 0);
for (index in 0...submatrix.underlying.length) {
var item = submatrix.underlying.inner[index];
hash |= (item > average ? 1 : 0) << index;
}

return ByteArray.from(hash);
}
}
7 changes: 7 additions & 0 deletions src/vision/ds/ByteArray.hx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package vision.ds;

import haxe.Int64;
import vision.tools.MathTools;
import haxe.Serializer;
import haxe.io.BytesData;
Expand All @@ -23,6 +24,12 @@ abstract ByteArray(Bytes) from Bytes to Bytes {
return bytes;
}

overload extern inline public static function from(value:Int64):ByteArray {
var bytes = new ByteArray(8);
bytes.setInt64(0, value);
return bytes;
}

/**
Generates a byte array of length 8, containing `value`.
The value is stored in little-endian format.
Expand Down
6 changes: 6 additions & 0 deletions src/vision/ds/Matrix2D.hx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ abstract Matrix2D(Array2D<Float>) to Array2D<Float> from Array2D<Float> {
return sum;
}

public inline function getAverage():Float {
var sum = 0.;
for (i in this.iterator())
sum += i;
return sum / this.inner.length;
}

/**
Multiplies this `Matrix2D` with a given scalar (a number).
Expand Down
7 changes: 7 additions & 0 deletions src/vision/tools/ArrayTools.hx
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,11 @@ class ArrayTools {
return s[floor(values.length / 2)];
}

public static function distanceTo<T>(array:Array<T>, to:Array<T>, distanceFunction:(T, T) -> Float) {
var sum = 0.;
for (i in 0...array.length - 1) {
sum += distanceFunction(array[i], array[i + 1]);
}
return sum;
}
}

0 comments on commit 11477b7

Please sign in to comment.