Skip to content

Commit 36134ac

Browse files
Initial project commit.
1 parent 4286edf commit 36134ac

8 files changed

+188
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/.idea/

composer.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "vajiral/php-image-compare",
3+
"description": "A light weight PHP class that can compare two (jpg/png) images to find if they are similar.",
4+
"keywords": ["image", "lightweight", "PHP library", "image compare", "jpg", "png"],
5+
"type": "library",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Vajira Lasantha",
10+
"email": "[email protected]"
11+
}
12+
],
13+
"minimum-stability": "dev",
14+
"require": {
15+
"php": "^5.5.9 || ^7.0",
16+
"ext-gd": "*"
17+
},
18+
"autoload": {
19+
"psr-4": {
20+
"BigV\\": "src/"
21+
}
22+
}
23+
}

demo/demo.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
use BigV\ImageCompare;
4+
5+
require __DIR__ . "/../vendor/autoload.php";
6+
7+
/**
8+
* These two images are almost the same so the hammered distance will be less than 10
9+
* Try it with images like below:
10+
* 1. Two slightly different images
11+
* 2. Two completely different images
12+
* 3. Two same images (returned value 0)
13+
* 4. Two same image but with different size/aspect ratio (returned value ~0)
14+
*/
15+
16+
$image = new ImageCompare();
17+
echo $image->compare(__DIR__ . '/image2-resize.jpg',__DIR__ . '/image2.jpg');
18+
19+
?>

demo/image1-bw.jpg

79.1 KB
Loading

demo/image1.jpg

36.2 KB
Loading

demo/image2-resize.jpg

64.1 KB
Loading

demo/image2.jpg

37.8 KB
Loading

src/ImageCompare.php

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?php
2+
namespace BigV;
3+
4+
class ImageCompare {
5+
6+
/**
7+
* Main function. Returns the hammering distance of two images' bit value.
8+
*
9+
* @param string $pathOne Path to image 1
10+
* @param string $pathTwo Path to image 2
11+
*
12+
* @return bool|int Hammering value on success. False on error.
13+
*/
14+
public function compare($pathOne, $pathTwo) {
15+
$i1 = $this->createImage($pathOne);
16+
$i2 = $this->createImage($pathTwo);
17+
18+
if (!$i1 || !$i2) {
19+
return false;
20+
}
21+
22+
$i1 = $this->resizeImage($pathOne);
23+
$i2 = $this->resizeImage($pathTwo);
24+
25+
imagefilter($i1, IMG_FILTER_GRAYSCALE);
26+
imagefilter($i2, IMG_FILTER_GRAYSCALE);
27+
28+
$colorMeanOne = $this->colorMeanValue($i1);
29+
$colorMeanTwo = $this->colorMeanValue($i2);
30+
31+
$bits1 = $this->bits($colorMeanOne);
32+
$bits2 = $this->bits($colorMeanTwo);
33+
34+
$hammeringDistance = 0;
35+
36+
for ($x = 0; $x < 64; $x++) {
37+
if ($bits1[$x] != $bits2[$x]) {
38+
$hammeringDistance++;
39+
}
40+
}
41+
42+
return $hammeringDistance;
43+
}
44+
45+
/**
46+
* Returns array with mime type and if its jpg or png. Returns false if it isn't jpg or png.
47+
*
48+
* @param string $path Path to image.
49+
*
50+
* @return array|bool
51+
*/
52+
private function mimeType($path) {
53+
$mime = getimagesize($path);
54+
$return = array($mime[0],$mime[1]);
55+
56+
switch ($mime['mime']) {
57+
case 'image/jpeg':
58+
$return[] = 'jpg';
59+
return $return;
60+
case 'image/png':
61+
$return[] = 'png';
62+
return $return;
63+
default:
64+
return false;
65+
}
66+
}
67+
68+
/**
69+
* Returns image resource or false if it's not jpg or png
70+
*
71+
* @param string $path Path to image
72+
*
73+
* @return bool|resource
74+
*/
75+
private function createImage($path) {
76+
$mime = $this->mimeType($path);
77+
78+
if ($mime[2] == 'jpg') {
79+
return imagecreatefromjpeg ($path);
80+
} else if ($mime[2] == 'png') {
81+
return imagecreatefrompng ($path);
82+
} else {
83+
return false;
84+
}
85+
}
86+
87+
/**
88+
* Resize the image to a 8x8 square and returns as image resource.
89+
*
90+
* @param string $path Path to image
91+
*
92+
* @return resource Image resource identifier
93+
*/
94+
private function resizeImage($path) {
95+
$mime = $this->mimeType($path);
96+
97+
$t = imagecreatetruecolor(8, 8);
98+
99+
$source = $this->createImage($path);
100+
101+
imagecopyresized($t, $source, 0, 0, 0, 0, 8, 8, $mime[0], $mime[1]);
102+
103+
return $t;
104+
}
105+
106+
/**
107+
* Returns the mean value of the colors and the list of all pixel's colors.
108+
*
109+
* @param resource $resource Image resource identifier
110+
*
111+
* @return array
112+
*/
113+
private function colorMeanValue($resource) {
114+
$colorList = array();
115+
$colorSum = 0;
116+
for ($a = 0; $a<8; $a++) {
117+
for ($b = 0; $b<8; $b++) {
118+
$rgb = imagecolorat($resource, $a, $b);
119+
$colorList[] = $rgb & 0xFF;
120+
$colorSum += $rgb & 0xFF;
121+
}
122+
}
123+
124+
return array($colorSum/64,$colorList);
125+
}
126+
127+
/**
128+
* Returns an array with 1 and zeros. If a color is bigger than the mean value of colors it is 1
129+
*
130+
* @param array $colorMean Color Mean details.
131+
*
132+
* @return array
133+
*/
134+
private function bits($colorMean) {
135+
$bits = array();
136+
foreach ($colorMean[1] as $color) {
137+
$bits[] = ($color >= $colorMean[0]) ? 1 : 0;
138+
}
139+
140+
return $bits;
141+
142+
}
143+
}
144+
145+
?>

0 commit comments

Comments
 (0)