Skip to content

Commit

Permalink
pHash calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
apangin committed Jul 12, 2016
1 parent b0233ce commit 2d9738e
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 13 deletions.
33 changes: 33 additions & 0 deletions src/one/webp/PHash.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2016 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package one.webp;

import java.nio.file.Files;
import java.nio.file.Paths;

public class PHash {

public static void main(String[] args) throws Exception {
if (args.length == 0) {
throw new IllegalArgumentException("Missing input file name");
}

byte[] src = Files.readAllBytes(Paths.get(args[0]));
long hash = WebP.phash(src);
System.out.println(Long.toHexString(hash));
}
}
5 changes: 5 additions & 0 deletions src/one/webp/WebP.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ public static byte[] convert(byte[] src, Params params) throws WebPException {
return convert1(src, params.longValue());
}

public static long phash(byte[] src) {
return phash0(src);
}

private static native int convert0(byte[] src, byte[] dst, long options);
private static native byte[] convert1(byte[] src, long options);
private static native long phash0(byte[] src);

static {
System.loadLibrary("onewebp");
Expand Down
11 changes: 11 additions & 0 deletions src/one/webp/native/jniwrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,14 @@ Java_one_webp_WebP_convert1(JNIEnv* env, jobject cls, jbyteArray srcArray, jlong
return array;
}
}

JNIEXPORT jlong JNICALL
Java_one_webp_WebP_phash0(JNIEnv* env, jobject cls, jbyteArray srcArray) {
jint srcSize = (*env)->GetArrayLength(env, srcArray);
jbyte* src = (*env)->GetPrimitiveArrayCritical(env, srcArray, NULL);

jlong result = image_phash(src, srcSize);

(*env)->ReleasePrimitiveArrayCritical(env, srcArray, src, JNI_ABORT);
return result;
}
131 changes: 120 additions & 11 deletions src/one/webp/native/onewebp.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@
* limitations under the License.
*/

#define _USE_MATH_DEFINES

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "turbojpeg.h"
#include "png.h"
#include "webp/encode.h"
#include "onewebp.h"


#define HASH_SIZE 64

typedef struct {
unsigned char* ptr;
const unsigned char* limit;
Expand Down Expand Up @@ -53,7 +58,7 @@ static int webp_writer(const uint8_t* data, size_t dataSize, const WebPPicture*
}


int decompress_jpeg(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params) {
static int decompress_jpeg(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params) {
tjhandle handle = tjInitDecompress();

int width, height, subsamp, colorspace;
Expand Down Expand Up @@ -84,7 +89,7 @@ int decompress_jpeg(unsigned char* src, unsigned long srcSize, RawImage* rawImag
return 0;
}

int decompress_png(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params) {
static int decompress_png(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params) {
png_image png = { NULL };
png.version = PNG_IMAGE_VERSION;

Expand All @@ -105,19 +110,27 @@ int decompress_png(unsigned char* src, unsigned long srcSize, RawImage* rawImage
return 0;
}

int decompress_image(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params) {
if (srcSize >= 4 && src[1] == 'P' && src[2] == 'N' && src[3] == 'G') {
return decompress_png(src, srcSize, rawImage, params);
} else {
return decompress_jpeg(src, srcSize, rawImage, params);
}
}

int compress_webp(unsigned char* dst, unsigned long dstSize, RawImage* rawImage, Params params) {
WebPConfig config;
WebPPicture picture;
Buffer buffer = { dst, dst + dstSize };
int maxWidth = params.maxWidth ? params.maxWidth : WEBP_MAX_DIMENSION;
int maxHeight = params.maxHeight ? params.maxHeight : WEBP_MAX_DIMENSION;

WebPConfig config;
WebPConfigInit(&config);
config.quality = (float)params.quality;
config.method = params.compression;
config.lossless = params.lossless;
config.thread_level = params.multithreaded;

WebPPicture picture;
WebPPictureInit(&picture);
picture.use_argb = 1;
picture.colorspace = WEBP_YUV420;
Expand All @@ -135,28 +148,25 @@ int compress_webp(unsigned char* dst, unsigned long dstSize, RawImage* rawImage,
maxWidth = 0;
}
if (!WebPPictureRescale(&picture, maxWidth, maxHeight)) {
WebPPictureFree(&picture);
return ERR_TRANSFORM;
}
}

if (!WebPEncode(&config, &picture)) {
WebPPictureFree(&picture);
return ERR_COMPRESS;
}

WebPPictureFree(&picture);
return (int)(buffer.ptr - dst);
}

int convert_to_webp(unsigned char* src, unsigned long srcSize,
unsigned char* dst, unsigned long dstSize,
Params params) {
RawImage rawImage;
int result;

if (srcSize >= 4 && src[1] == 'P' && src[2] == 'N' && src[3] == 'G') {
result = decompress_png(src, srcSize, &rawImage, params);
} else {
result = decompress_jpeg(src, srcSize, &rawImage, params);
}
int result = decompress_image(src, srcSize, &rawImage, params);

if (result == 0) {
result = compress_webp(dst, dstSize, &rawImage, params);
Expand All @@ -165,3 +175,102 @@ int convert_to_webp(unsigned char* src, unsigned long srcSize,

return result;
}

static void argb_to_grayscale(unsigned int* src, int stride, float* grayscale) {
int i, j;
for (i = 0; i < HASH_SIZE; i++) {
for (j = 0; j < HASH_SIZE; j++) {
unsigned int argb = src[i * stride + j];
unsigned int r = (argb >> 16) & 0xff;
unsigned int g = (argb >> 8) & 0xff;
unsigned int b = (argb) & 0xff;
grayscale[i * HASH_SIZE + j] = r * 0.299f + g * 0.587f + b * 0.114f;
}
}
}

static void dct_vector(float* vector) {
float transformed[HASH_SIZE];
int i, j;
for (i = 0; i < HASH_SIZE; i++) {
float sum = 0;
for (j = 0; j < HASH_SIZE; j++) {
sum += vector[j] * cos(i * M_PI * (j + 0.5) / HASH_SIZE);
}
sum *= sqrt(2.0f / HASH_SIZE);
transformed[i] = (i == 0) ? sum * M_SQRT1_2 : sum;
}
memcpy(vector, transformed, sizeof(transformed));
}

static void dct_8x8(float* pixels, float* transformed) {
int i, j;
for (i = 0; i < HASH_SIZE; i++) {
float* row = &pixels[i * HASH_SIZE];
dct_vector(row);
}

for (i = 0; i < 8; i++) {
float col[HASH_SIZE];
for (j = 0; j < HASH_SIZE; j++) {
col[j] = pixels[j * HASH_SIZE + i];
}
dct_vector(col);
for (j = 0; j < 8; j++) {
transformed[i * 8 + j] = col[j];
}
}
}

static float median(float* pixels) {
float sum = 0;
int i;
for (i = 0; i < 64; i++) {
sum += pixels[i];
}
return sum / 64;
}

static unsigned long long bitmask(float* pixels, float median) {
unsigned long long mask = 0;
int i;
for (i = 0; i < 64; i++) {
if (pixels[i] > median) {
mask |= 1ULL << i;
}
}
return mask;
}

unsigned long long image_phash(unsigned char* src, unsigned long srcSize) {
RawImage rawImage;
Params params = {HASH_SIZE, HASH_SIZE, 0, 0, 1, 0, 0};
if (decompress_image(src, srcSize, &rawImage, params)) {
return 0;
}

WebPPicture picture;
WebPPictureInit(&picture);
picture.use_argb = 1;
picture.width = rawImage.width;
picture.height = rawImage.height;
picture.argb = (uint32_t*)rawImage.argb;
picture.argb_stride = rawImage.width;

if (!WebPPictureRescale(&picture, HASH_SIZE, HASH_SIZE)) {
WebPPictureFree(&picture);
free(rawImage.argb);
return 0;
}

float grayscale[HASH_SIZE * HASH_SIZE];
argb_to_grayscale(picture.argb, picture.argb_stride, grayscale);
WebPPictureFree(&picture);
free(rawImage.argb);

float transformed[64];
dct_8x8(grayscale, transformed);

float m = median(transformed);
return bitmask(transformed, m);
}
5 changes: 3 additions & 2 deletions src/one/webp/native/onewebp.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ typedef struct {
} RawImage;


int decompress_jpeg(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params);
int decompress_png(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params);
int decompress_image(unsigned char* src, unsigned long srcSize, RawImage* rawImage, Params params);
int compress_webp(unsigned char* dst, unsigned long dstSize, RawImage* rawImage, Params params);

int convert_to_webp(unsigned char* src, unsigned long srcSize,
unsigned char* dst, unsigned long dstSize,
Params params);

unsigned long long image_phash(unsigned char* src, unsigned long srcSize);

0 comments on commit 2d9738e

Please sign in to comment.