/*
pHash, the open source perceptual hash library
Copyright (C) 2010 Aetilius, Inc.
All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Evan Klinger - eklinger@phash.org
David Starkweather - dstarkweather@phash.org
*/
#include
#include "pHash.h"
#include "pHashPro-jni.h"
jfieldID radialImHash_hash = NULL;
jfieldID dctImHash_hash = NULL;
jfieldID mhImHash_hash = NULL;
jfieldID vidHash_hash = NULL;
jfieldID audioHash_hash = NULL;
jfieldID hash_filename = NULL;
jclass radialImClass = NULL;
jclass dctImClass = NULL;
jclass mhImClass = NULL;
jclass vidClass = NULL;
jclass audioClass = NULL;
jmethodID radialImCtor = NULL;
jmethodID dctImCtor = NULL;
jmethodID mhImCtor = NULL;
jmethodID vidCtor = NULL;
jmethodID audioCtor = NULL;
typedef enum ph_jni_hash_types {
IMAGE_HASH,
VIDEO_HASH,
AUDIO_HASH,
TEXT_HASH
} jniHashType;
typedef struct ph_jni_hash_classes {
jclass *cl;
jniHashType kind;
jmethodID *ctor;
jfieldID *hashID;
} jniHashes;
const static jniHashes hashes[] = {
{&radialImClass, IMAGE_HASH, &radialImCtor, &radialImHash_hash},
{&mhImClass, IMAGE_HASH, &mhImCtor, &mhImHash_hash},
{&dctImClass, IMAGE_HASH, &dctImCtor, &dctImHash_hash},
{&vidClass, VIDEO_HASH, &vidCtor, &vidHash_hash},
{&audioClass, AUDIO_HASH, &audioCtor, &audioHash_hash},
};
JNIEXPORT void JNICALL Java_org_phash_pHash_pHashInit(JNIEnv *e, jclass cl) {
radialImClass =
(jclass)e->NewGlobalRef(e->FindClass("org/phash/RadialImageHash"));
dctImClass =
(jclass)e->NewGlobalRef(e->FindClass("org/phash/DCTImageHash"));
mhImClass = (jclass)e->NewGlobalRef(e->FindClass("org/phash/MHImageHash"));
vidClass = (jclass)e->NewGlobalRef(e->FindClass("org/phash/VideoHash"));
dctImHash_hash = e->GetFieldID(dctImClass, "hash", "J");
radialImHash_hash = e->GetFieldID(radialImClass, "hash", "[B");
mhImHash_hash = e->GetFieldID(mhImClass, "hash", "[B");
audioHash_hash = e->GetFieldID(audioClass, "hash", "[I");
vidHash_hash = e->GetFieldID(vidClass, "hash", "[J");
hash_filename = e->GetFieldID(e->FindClass("org/phash/Hash"), "filename",
"Ljava/lang/String;");
dctImCtor = e->GetMethodID(dctImClass, "", "()V");
radialImCtor = e->GetMethodID(radialImClass, "", "()V");
mhImCtor = e->GetMethodID(mhImClass, "", "()V");
vidCtor = e->GetMethodID(vidClass, "", "()V");
audioCtor = e->GetMethodID(audioClass, "", "()V");
}
JNIEXPORT void JNICALL Java_org_phash_pHash_cleanup(JNIEnv *e, jclass cl) {
e->DeleteGlobalRef(radialImClass);
e->DeleteGlobalRef(mhImClass);
e->DeleteGlobalRef(dctImClass);
e->DeleteGlobalRef(vidClass);
e->DeleteGlobalRef(audioClass);
}
JNIEXPORT jdouble JNICALL Java_org_phash_pHash_imageDistance(JNIEnv *e,
jclass cl,
jobject hash1,
jobject hash2) {
if (e->IsInstanceOf(hash1, dctImClass) &&
e->IsInstanceOf(hash2, dctImClass)) {
ulong64 imHash, imHash2;
imHash = (ulong64)e->GetLongField(hash1, dctImHash_hash);
imHash2 = (ulong64)e->GetLongField(hash2, dctImHash_hash);
return ph_hamming_distance(imHash, imHash2);
} else if (e->IsInstanceOf(hash1, mhImClass) &&
e->IsInstanceOf(hash2, mhImClass)) {
jbyteArray h = (jbyteArray)e->GetObjectField(hash1, mhImHash_hash);
jbyteArray h2 = (jbyteArray)e->GetObjectField(hash2, mhImHash_hash);
int N = e->GetArrayLength(h);
int N2 = e->GetArrayLength(h2);
jbyte *hash = e->GetByteArrayElements(h, NULL);
jbyte *hash2 = e->GetByteArrayElements(h2, NULL);
double hd =
ph_hammingdistance2((uint8_t *)hash, N, (uint8_t *)hash2, N2);
e->ReleaseByteArrayElements(h, hash, 0);
e->ReleaseByteArrayElements(h2, hash2, 0);
return hd;
} else if (e->IsInstanceOf(hash1, radialImClass) &&
e->IsInstanceOf(hash2, radialImClass)) {
jbyteArray h = (jbyteArray)e->GetObjectField(hash1, radialImHash_hash);
jbyteArray h2 = (jbyteArray)e->GetObjectField(hash2, radialImHash_hash);
int N = e->GetArrayLength(h);
int N2 = e->GetArrayLength(h2);
jbyte *hash = e->GetByteArrayElements(h, NULL);
jbyte *hash2 = e->GetByteArrayElements(h2, NULL);
RadialHash x = {(uint8_t *)hash, N}, y = {(uint8_t *)hash2, N2};
double pcc = -1;
ph_peakcrosscorr(x, y, &pcc);
e->ReleaseByteArrayElements(h, hash, 0);
e->ReleaseByteArrayElements(h2, hash2, 0);
return pcc;
}
return -1;
}
#ifdef HAVE_AUDIO_HASH
JNIEXPORT jdouble JNICALL Java_org_phash_pHash_audioDistance(
JNIEnv *e, jclass cl, jobject audioHash1, jobject audioHash2) {
if (audioHash1 == NULL || audioHash2 == NULL) {
return (jdouble)-1.0;
}
const float threshold = 0.30;
const int block_size = 256;
int Nc;
jdouble maxC = 0.0;
double *pC;
jint hash1_len, hash2_len;
jintArray hash1, hash2;
hash1 = (jintArray)e->GetObjectField(audioHash1, audioHash_hash);
hash2 = (jintArray)e->GetObjectField(audioHash2, audioHash_hash);
hash1_len = e->GetArrayLength(hash1);
hash2_len = e->GetArrayLength(hash2);
if (hash1_len <= 0 || hash2_len <= 0) {
return (jdouble)-1.0;
}
uint32_t *hash1_n, *hash2_n;
hash1_n = (uint32_t *)e->GetIntArrayElements(hash1, 0);
hash2_n = (uint32_t *)e->GetIntArrayElements(hash2, 0);
pC = ph_audio_distance_ber(hash1_n, hash1_len, hash2_n, hash2_len,
threshold, block_size, &Nc);
e->ReleaseIntArrayElements(hash1, (jint *)hash1_n, 0);
e->ReleaseIntArrayElements(hash2, (jint *)hash2_n, 0);
maxC = 0.0;
for (int j = 0; j < Nc; j++) {
if (pC[j] > maxC) {
maxC = pC[j];
}
}
delete[] pC;
return maxC;
}
#endif
JNIEXPORT jobject JNICALL Java_org_phash_pHash_dctImageHash(JNIEnv *e,
jclass cl,
jstring f) {
const char *file = e->GetStringUTFChars(f, 0);
ulong64 hash;
int ret = ph_dct_imagehash(file, &hash);
if (ret != 0) {
e->ReleaseStringUTFChars(f, file);
return NULL;
}
jobject imageHash = e->NewObject(dctImClass, dctImCtor);
e->SetObjectField(imageHash, hash_filename, f);
e->SetLongField(imageHash, dctImHash_hash, (jlong)hash);
e->ReleaseStringUTFChars(f, file);
return imageHash;
}
JNIEXPORT jobject JNICALL Java_org_phash_pHash_mhImageHash(JNIEnv *e, jclass cl,
jstring f) {
const char *file = e->GetStringUTFChars(f, 0);
int N;
uint8_t *hash = ph_mh_imagehash(file, &N, 2, 1);
jobject imageHash = NULL;
if (hash && N > 0) {
imageHash = e->NewObject(mhImClass, mhImCtor);
e->SetObjectField(imageHash, hash_filename, f);
jbyteArray hashVals = e->NewByteArray(N);
e->SetByteArrayRegion(hashVals, 0, N, (jbyte *)hash);
e->SetObjectField(imageHash, mhImHash_hash, hashVals);
free(hash);
}
e->ReleaseStringUTFChars(f, file);
return imageHash;
}
JNIEXPORT jobject JNICALL Java_org_phash_pHash_radialImageHash(JNIEnv *e,
jclass cl,
jstring f) {
const char *file = e->GetStringUTFChars(f, 0);
RadialHash rh;
ph_radial_imagehash(file, &rh, 1.0, 180);
jobject imageHash = NULL;
if (rh.coeffs && rh.size > 0) {
imageHash = e->NewObject(radialImClass, radialImCtor);
e->SetObjectField(imageHash, hash_filename, f);
jbyteArray hashVals = e->NewByteArray(rh.size);
e->SetByteArrayRegion(hashVals, 0, rh.size, (jbyte *)rh.coeffs);
e->SetObjectField(imageHash, radialImHash_hash, hashVals);
free(rh.coeffs);
}
e->ReleaseStringUTFChars(f, file);
return imageHash;
}
#ifdef HAVE_VIDEO_HASH
JNIEXPORT jdouble JNICALL Java_org_phash_pHash_videoDistance(
JNIEnv *e, jclass cl, jobject vidHash1, jobject vidHash2, jint thresh) {
if (vidHash1 == NULL || vidHash2 == NULL) {
return (jdouble)-1.0;
}
jint hash1_len, hash2_len;
jlongArray hash1, hash2;
hash1 = (jlongArray)e->GetObjectField(vidHash1, vidHash_hash);
hash2 = (jlongArray)e->GetObjectField(vidHash2, vidHash_hash);
hash1_len = e->GetArrayLength(hash1);
hash2_len = e->GetArrayLength(hash2);
if (hash1_len <= 0 || hash2_len <= 0) {
return (jdouble)-1.0;
}
ulong64 *hash1_n, *hash2_n;
hash1_n = (ulong64 *)e->GetLongArrayElements(hash1, 0);
hash2_n = (ulong64 *)e->GetLongArrayElements(hash2, 0);
jdouble sim =
ph_dct_videohash_dist(hash1_n, hash1_len, hash2_n, hash2_len, thresh);
e->ReleaseLongArrayElements(hash1, (jlong *)hash1_n, 0);
e->ReleaseLongArrayElements(hash2, (jlong *)hash2_n, 0);
return sim;
}
JNIEXPORT jobject JNICALL Java_org_phash_pHash_videoHash(JNIEnv *e, jclass cl,
jstring f,
jdouble sigma) {
const char *file = e->GetStringUTFChars(f, 0);
int len;
ulong64 *hashes = NULL;
int ret = ph_dct_videohash_file(file, &hashes, &len, sigma);
jobject videoHash = e->NewObject(vidClass, vidCtor);
e->SetObjectField(videoHash, hash_filename, f);
jlongArray hashVals = e->NewLongArray(len);
e->SetLongArrayRegion(hashVals, 0, len, (jlong *)hashes);
e->SetObjectField(videoHash, vidHash_hash, hashVals);
free(hashes);
e->ReleaseStringUTFChars(f, file);
return videoHash;
}
#endif
#ifdef HAVE_AUDIO_HASH
JNIEXPORT jobject JNICALL Java_org_phash_pHash_audioHash(JNIEnv *e, jclass cl,
jstring f) {
jintArray ret = NULL;
const int sr = 8000;
const int channels = 1;
int N;
int nbframes;
float *buf = NULL;
unsigned int *hash = NULL;
const char *file = e->GetStringUTFChars(f, 0);
buf = ph_readaudio(file, sr, channels, NULL, &N);
if (!buf) {
e->ReleaseStringUTFChars(f, file);
return NULL;
}
hash = ph_audiohash(buf, N, sr, &nbframes);
if (!hash || nbframes <= 0) {
free(buf);
return NULL;
}
free(buf);
jobject audioHash = e->NewObject(audioClass, audioCtor);
e->SetObjectField(audioHash, hash_filename, f);
e->ReleaseStringUTFChars(f, file);
jintArray hashVals = e->NewIntArray(nbframes);
e->SetIntArrayRegion(hashVals, 0, nbframes, (jint *)hash);
e->SetObjectField(audioHash, audioHash_hash, hashVals);
free(hash);
return audioHash;
}
#endif