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 12 years, 7 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 12 years, 7 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 12 years, 7 months ago

Not sure of a use? lol

link2x101 12 years, 7 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 12 years, 7 months ago

Updated.

link2x101 12 years, 7 months ago

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

Josea 12 years, 7 months ago

Blogs are made to die.