Another version of the image comparison algorithm
This article hung for a month in my drafts, until someone finally brought me karma to tone. I don’t know who, but thank you
Today, once again logging into the hub, I came across this interesting article. It describes an image hashing algorithm. When I read this article, I had the idea how to change this algorithm so that it eats images that vary greatly, for example, brightness (but the images themselves are identical).
Perhaps this method has already existed for a long time and has been described by someone for a long time, well, in this case, at least I have ready-made code.
The algorithm is that we need to write to the array the difference between each i-th and (i + 1) -th pixel. Thus, this array is a hash for our image (if necessary, you can bring it into a string form). That is, based on this, we believe that our image is not a set of pixels of a certain color, but rather a collection of differences between these pixels.
The test results of such an algorithm turned out to be rather entertaining, but they will be lower after parsing the code.
Now we will analyze this algorithm on the code.
I wrote a simple class that can compare any number of images, giving the difference between each of them.
Here is the class itself, if you like, you can stop at it, or you can read further what is here and what is needed.
In the constructor, the _getPixelsDiff () method is called on each image, and its result is put into the Images array. This method performs the following manipulations:
By the way, based on this code, the largest difference in images can be 1701 (63 * 3 * 9, correct if it is not).
And now the tests.
The first pair of pictures: one and two . The first picture is the Air Jordan logo. The second is a parody of the first performed by Bender. Agree, the eyes are pretty similar pictures. And our program gives them 68 difference points out of 1701. That is, the probability of their identity is approximately 96.1%.
The second pair of pictures: one and two. It produces a difference of 266, although their color scheme is quite similar. By the way, it turns out that these figures are identical with a probability of 85%. So the bar (sorry, threshold) must be set quite high.
Here's a couple more: one and two . Difference 52.
In general, I do not think this method is ideal, but at least I think it has the right to life.
UPD Comparison of this image and this . The contrast of the second was greatly changed as we see. The difference is 70 parrots out of 1701.
Today, once again logging into the hub, I came across this interesting article. It describes an image hashing algorithm. When I read this article, I had the idea how to change this algorithm so that it eats images that vary greatly, for example, brightness (but the images themselves are identical).
Perhaps this method has already existed for a long time and has been described by someone for a long time, well, in this case, at least I have ready-made code.
The algorithm is that we need to write to the array the difference between each i-th and (i + 1) -th pixel. Thus, this array is a hash for our image (if necessary, you can bring it into a string form). That is, based on this, we believe that our image is not a set of pixels of a certain color, but rather a collection of differences between these pixels.
The test results of such an algorithm turned out to be rather entertaining, but they will be lower after parsing the code.
Now we will analyze this algorithm on the code.
I wrote a simple class that can compare any number of images, giving the difference between each of them.
Here is the class itself, if you like, you can stop at it, or you can read further what is here and what is needed.
class ImagesComparer
{
const
BAD_ARGS = 1,
UNSUPPORTED_FILETYPE = 2,
ERROR_OPEN = 3;
public $Images = array();
private
$_types = array('', 'gif', 'jpeg', 'png', '', '', 'wbmp', '', '', '', '', '');
public $CompareWithFirst = false;
public function __construct($Image1, $Image2 = null)
{
if (func_num_args() > 2)
$Images = func_get_args();
else if (is_array($Image1))
$Images = $Image1;
else
$Images = array($Image1, $Image2);
foreach ($Images as $Image)
{
if (is_string($Image))
$this->_openImage($Image);
else if (is_resource($Image))
$this->Images[] = array($this->_getPixelsDiff($Image), array());
else
throw new Exception('Bad arguments.', self::BAD_ARGS);
}
}
private function _getImageType($Image)
{
$Type = getimagesize($Image);
if (!$Type = $this->_types[$Type[2]])
throw new Exception('Image have an unsupported file type.', self::UNSUPPORTED_FILETYPE);
return 'imagecreatefrom' . $Type;
}
private function _openImage($Image)
{
$Type = $this->_getImageType($Image);
$Image = $Type($Image);
if (!$Image)
throw new Exception('Error opening image.', self::ERROR_OPEN);
$this->Images[] = array($this->_getPixelsDiff($Image), array());
imagedestroy($Image);
}
private function _getPixelsDiff($Image)
{
$Sample = imagecreatetruecolor(8, 8);
imagecopyresampled($Sample, $Image, 0, 0, 0, 0, 8, 8, imagesx($Image), imagesy($Image));
$Pixels = array();
$Color = array(0, 0, 0);
for ($y = 0; $y < 8; $y++)
{
for ($x = 0; $x < 8; $x++)
{
$Color1 = imagecolorat($Sample, $x, $y);
$Color1 = $this->_scale255To9(array(
($Color1 >> 16) & 0xFF,
($Color1 >> 8) & 0xFF,
$Color & 0xFF
));
if ($x != 0 || $y != 0)
{
$Pixels[] = array(
$Color1[0] - $Color[0],
$Color1[1] - $Color[1],
$Color1[2] - $Color[2]
);
}
$Color = $Color1;
}
}
imagedestroy($Sample);
return $Pixels;
}
private function _scale255To9($NumArr)
{
return array(
round($NumArr[0] / 28.3),
round($NumArr[1] / 28.3),
round($NumArr[2] / 28.3)
);
}
private function _getDiff($Img1, $Img2)
{
$Diff = 0;
for ($i = 0; $i < 63; $i++)
{
$Diff += abs($this->Images[$Img1][0][$i][0] - $this->Images[$Img2][0][$i][0]);
$Diff += abs($this->Images[$Img1][0][$i][1] - $this->Images[$Img2][0][$i][1]);
$Diff += abs($this->Images[$Img1][0][$i][2] - $this->Images[$Img2][0][$i][2]);
}
return $Diff;
}
public function Compare()
{
$count = count($this->Images);
if ($this->CompareWithFirst)
{
for ($i = 1; $i < $count; $i++)
{
$this->Images[0][1][$i] = $this->_getDiff(0, $i);
}
}
else
{
for ($i = 0; $i < $count; $i++)
{
for ($k = $i + 1; $k < $count; $k++)
{
//echo "\r\n
" .
$this->Images[$k][1][$i] =
$this->Images[$i][1][$k] = $this->_getDiff($i, $k);
}
}
}
}
}
In the constructor, the _getPixelsDiff () method is called on each image, and its result is put into the Images array. This method performs the following manipulations:
- Reduces the image to 8x8.
- Creates an array for flowers.
- It goes through each pixel of the image:
- Takes its color in RGB.
- Each channel divides by 28.3 and rounds, so that the maximum value of the channel is equal to 9.
- Subtracts the value of the previous pixel from each channel.
- The result is put into an array and returned.
By the way, based on this code, the largest difference in images can be 1701 (63 * 3 * 9, correct if it is not).
And now the tests.
The first pair of pictures: one and two . The first picture is the Air Jordan logo. The second is a parody of the first performed by Bender. Agree, the eyes are pretty similar pictures. And our program gives them 68 difference points out of 1701. That is, the probability of their identity is approximately 96.1%.
The second pair of pictures: one and two. It produces a difference of 266, although their color scheme is quite similar. By the way, it turns out that these figures are identical with a probability of 85%. So the bar (sorry, threshold) must be set quite high.
Here's a couple more: one and two . Difference 52.
In general, I do not think this method is ideal, but at least I think it has the right to life.
UPD Comparison of this image and this . The contrast of the second was greatly changed as we see. The difference is 70 parrots out of 1701.