Perceptual Hashing [+Game/Example]

Posted by link2x101 on Sept. 26, 2011, 5:35 p.m.

As the title says.

But there are some wrong-doings here.

1. It doesn't really hash, that is, you don't get any kind of alphanumeric key… you get a 64-character code (comprised of 1s and 0s. :3).

2. It's low quality, and would need some modification for more serious work, but this is Game Maker, after all.

Anyway, lets go back in time, to last night…

I came onto IRC, remembering someone talk of something they were working on, for comparing images, and I needed something like that for a project I'm working on, so I asked about, and Ling answered by telling me a bit about Perceptual Hashing, which creates similar hashes for similar images.

I looked more into it, and began porting the basic theory.

Now that I've done so (with a basic Hamming Distance-like thing as well), I've decided to release the code.

It's two small scripts, pretty much fully commented.

Licensed under CC-BY-SA. :3

Please excuse my bad coding habits and overall bad coding. It works, at least.

phash();

Returns a 64-character string.

/***************************************************

  Perceptual Hash in Game Maker
  
  Script written by Nick Simmons using the basic
  structure from [ <a rel="nofollow" href="http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html">http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html</a> ].
  
  This code may be freely distributed and edited under
  the Creative Commons BY-SA liscense.
  
  Usage: phash(image);
  
  ***************************************************/

var temp_image work_surface work_surface2 average;
average = 0;
main_pixel[0,0] = 0;
output = "";

temp_image = argument0;

work_surface = surface_create(8,8);     // Create a small surface to draw/read from.

surface_set_target(work_surface);       // Set surface one as the draw target.
draw_sprite_stretched(temp_image,0,0,0,8,8); // Draw the image, stretched down to size.


// Here's the fun part (part one), use the colors to convert to grayscale.

for (j=0; j<8; j+=1;) { // All the way down the image.
for (i=0; i<8; i+=1;) { // All the way across.
_a = surface_getpixel(work_surface,i,j); // Get the color.

_b[0] = color_get_red(_a);    // Separate the color out.
_b[1] = color_get_green(_a);  // ^
_b[2] = color_get_blue(_a);   // ^

_c = (_b[0]+_b[1]+_b[2])/765; // Add them all together, and average it out to make it 'grayscale'.

main_pixel[i,j] = _c; // Store that for now.
}
}


// Fun part (part two), average the grayscale.
for (j=0; j<8; j+=1;) { // All the way down the image.
for (i=0; i<8; i+=1;) { // All the way across.
average += main_pixel[i,j]; // Add the value to the overall average.
}
}
average = average/64; // Divide that by the amount of pixels (8x8 = 64).


// Fun part (part three), convert to one-bit using the average.
for (j=0; j<8; j+=1;) { // All the way down the image.
for (i=0; i<8; i+=1;) { // All the way across.
if main_pixel[i,j] > average { // Make the image 'one bit'.
main_pixel[i,j] = 1;           // ^
} else {                       // ^
main_pixel[i,j] = 0;           // ^
}
}
}


// More can be done here, but for now just make it a 64-character string.
for (j=0; j<8; j+=1;) { // All the way down the image.
for (i=0; i<8; i+=1;) { // All the way across.
output = output + string(main_pixel[i,j]);
}
}

// Then make it a 32-character string, from that.
//output = string_copy(output,0,32) + "#" + string_copy(output,32,32); 
// Note: I have this here just because I personally used/use it this way, since the #
//       is the same for all hashes, it doesn't throw it off by enough to be noticible.
//       If you really have an issue with that, just leave it uncommented.


surface_reset_target();     // Fix drawing.
surface_free(work_surface); // Delete the surface.


return output;

And…

hdistance();

Returns the percent of similarity in two strings.

/***************************************************

  (Psuedo) Hamming Distance in Game Maker
  
  Script written by Nick Simmons.
  
  This code may be freely distributed and edited under
  the Creative Commons BY-SA liscense.
  
  Usage: hdistance(string1, string2);
  
  Warning: This script was intended for use with phash();
  other uses may require modification.
  
  Also, in this case, the script currently gives a
  percentage. (Or a -1 in case of failure.)
  
  
  Requirements:
    - Must be strings.
    - Must be the same length.
  
  ***************************************************/
  
  var input_a input_b threshold string_a string_b length output;
  
  input_a = string(argument0);
  input_b = string(argument1);
  
  string_a[0] = "";
  string_b[0] = "";
  
  output = 0;
  
  length = string_length(input_a); // Get the length.
  if length != string_length(input_b) {return -1; break;} // If the lengths do not match, stop now.
  
  for (i=0; i<length; i+=1;) { // For the entire length of both strings.
  string_a[i] = string_copy(input_a,i,1); // Copy them into an array.
  string_b[i] = string_copy(input_b,i,1); // ^
  }
  
  for (i=0; i<length; i+=1;) { // For the entire length of both strings.
  if string_a[i] = string_b[i] {output += 1;}; // Check them against eachother, and add 1 to the output if they match.
  }
  
  output = output / length; // Divide by the length to get a percentage.
   
  return output;

Enjoy, perhaps.


And here we have… a game I made. :D

It was for a health project (oh irony) on OCD.

I made use of the above thing in the game.

Techno-Jumper - Platformer

5 levels. SFX made with SFXR. Music made with FL Studio.

DOWNLOAD o.o

Comments

sirxemic 13 years, 11 months ago

Pretty simple algorithm I see, but I'd like to see some test results… how well does this algorithm perform? I quickly glanced over your source material and noticed you are converting the source color to gray scale sort of wrong. Usually converting an RGB color to a gray one is done by 0.3*R+0.59*G+0.11*B. I am not sure what kind of effect this has on the algorithm, but you should try it out!

Furthermore, I became interested in perceptual hashing a few weeks back as well and found pHash. Try to implement THAT in GM :3 :3

link2x101 13 years, 11 months ago

Well it was written simply and such so I probably did things wrong, but it performs pretty fast, considering the use of get pixel, but Game Maker is a bit iffy about scaling in that it's giving different results sometimes…

And I could certainly fix the greyscale, but I'm just lazy… XD

As far as pHash goes, no. I don't know much C and do not have any environment to work with so yeah… o.o

Alert Games 13 years, 11 months ago

Not sure of a use? lol

link2x101 13 years, 11 months ago

Well I have one, so I made it. XD Anyway, I guess it is a bit out of place, for making games, but here it is anyway. O.o

link2x101 13 years, 11 months ago

Updated.

link2x101 13 years, 11 months ago

WHY MUST MY BLOGS ALWAYS DIE!? </raeg>

Josea 13 years, 11 months ago

Blogs are made to die.