Skip to content

Commit

Permalink
Merge pull request #34 from jenssegers/imagick
Browse files Browse the repository at this point in the history
Imagick
  • Loading branch information
jenssegers authored May 5, 2018
2 parents fc4ef4e + 2041592 commit 478fec0
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 127 deletions.
5 changes: 1 addition & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
language: php

php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- hhvm

sudo: false

Expand All @@ -20,4 +17,4 @@ script:
- ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml

after_success:
- sh -c 'if [ "$TRAVIS_PHP_VERSION" != "hhvm" ]; then php vendor/bin/coveralls -v; fi;'
- php vendor/bin/coveralls -v
10 changes: 6 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@
],
"require": {
"php": ">=5.4.0",
"ext-gd": "*"
"ext-gd": "*",
"intervention/image": "^2.4"
},
"require-dev": {
"phpunit/phpunit": "^4.0|^5.0",
"satooshi/php-coveralls": "^0.6"
"phpunit/phpunit": "^4.0|^5.0|^6.0|^7.0",
"satooshi/php-coveralls": "^2.0"
},
"suggest": {
"ext-gmp": "It can speed up the image hash."
"ext-gmp": "GD or ImageMagick is required",
"ext-imagick": "GD or ImageMagick is required"
},
"autoload": {
"psr-4": {
Expand Down
105 changes: 44 additions & 61 deletions src/ImageHash.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php namespace Jenssegers\ImageHash;

use Exception;
use InvalidArgumentException;
use Intervention\Image\Image;
use Intervention\Image\ImageManager;
use Jenssegers\ImageHash\Implementations\DifferenceHash;
use RuntimeException;

class ImageHash
{
Expand All @@ -28,57 +29,53 @@ class ImageHash
*/
protected $mode;

/**
* @var Image
*/
private $driver;

/**
* Constructor.
*
* @param Implementation $implementation
* @param string $mode
* @param string $mode
* @param ImageManager $driver
*/
public function __construct(Implementation $implementation = null, $mode = self::HEXADECIMAL)
{
public function __construct(
Implementation $implementation = null,
$mode = self::HEXADECIMAL,
ImageManager $driver = null
) {
$this->implementation = $implementation ?: new DifferenceHash;
$this->mode = $mode;
$this->driver = $driver ?: $this->defaultDriver();
}

/**
* Calculate a perceptual hash of an image file.
* Calculate a perceptual hash of an image.
*
* @param mixed $resource GD2 resource or filename
* @param mixed $image
* @return int
*/
public function hash($resource)
public function hash($image)
{
$destroy = false;
$image = $this->driver->make($image);

if (! is_resource($resource)) {
$resource = $this->loadImageResource($resource);
$destroy = true;
}

$hash = $this->implementation->hash($resource);

if ($destroy) {
$this->destroyResource($resource);
}
$hash = $this->implementation->hash($image);

return $this->formatHash($hash);
}

/**
* Calculate a perceptual hash of an image string.
*
* @deprecated
* @param mixed $data Image data
* @return string
*/
public function hashFromString($data)
{
$resource = $this->createResource($data);

$hash = $this->implementation->hash($resource);

$this->destroyResource($resource);

return $this->formatHash($hash);
return $this->hash($data);
}

/**
Expand Down Expand Up @@ -107,7 +104,7 @@ public function distance($hash1, $hash2)
{
if (extension_loaded('gmp')) {
if ($this->mode === self::HEXADECIMAL) {
$dh = gmp_hamdist('0x'.$hash1, '0x'.$hash2);
$dh = gmp_hamdist('0x' . $hash1, '0x' . $hash2);
} else {
$dh = gmp_hamdist($hash1, $hash2);
}
Expand Down Expand Up @@ -137,7 +134,7 @@ public function distance($hash1, $hash2)
*/
public function hexdec($hex)
{
if (strlen($hex) == 16 && hexdec($hex[0]) > 8) {
if (strlen($hex) === 16 && hexdec($hex[0]) > 8) {
list($higher, $lower) = array_values(unpack('N2', hex2bin($hex)));
return $higher << 32 | $lower;
}
Expand All @@ -146,43 +143,12 @@ public function hexdec($hex)
}

/**
* Get a GD2 resource from file.
*
* @param string $file
* @return resource
*/
protected function loadImageResource($file)
{
try {
return $this->createResource(file_get_contents($file));
} catch (Exception $e) {
throw new InvalidArgumentException("Unable to load file: $file");
}
}

/**
* Get a GD2 resource from string.
*
* @param string $data
* @return resource
* @return Image
*/
protected function createResource($data)
{
try {
return imagecreatefromstring($data);
} catch (Exception $e) {
throw new InvalidArgumentException('Unable to create GD2 resource');
}
}

/**
* Destroy GD2 resource.
*
* @param resource $resource
*/
protected function destroyResource($resource)
{
imagedestroy($resource);
return $this->driver->make($data);
}

/**
Expand All @@ -195,4 +161,21 @@ protected function formatHash($hash)
{
return $this->mode === static::HEXADECIMAL ? dechex($hash) : $hash;
}

/**
* @return ImageManager
* @throws RuntimeException
*/
protected function defaultDriver()
{
if (extension_loaded('gd')) {
return new ImageManager(['driver' => 'gd']);
}

if (extension_loaded('imagick')) {
return new ImageManager(['driver' => 'imagick']);
}

throw new RuntimeException('Please install GD or ImageMagick');
}
}
6 changes: 4 additions & 2 deletions src/Implementation.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<?php namespace Jenssegers\ImageHash;

use Intervention\Image\Image;

interface Implementation
{
/**
* Calculate the hash for the given resource.
*
* @param resource $resource
* @param Image $image
* @return int
*/
public function hash($resource);
public function hash(Image $image);
}
15 changes: 6 additions & 9 deletions src/Implementations/AverageHash.php
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
<?php namespace Jenssegers\ImageHash\Implementations;

use Intervention\Image\Image;
use Jenssegers\ImageHash\Implementation;

class AverageHash implements Implementation
{
const SIZE = 8;

/**
* {@inheritDoc}
* @inheritdoc
*/
public function hash($resource)
public function hash(Image $image)
{
// Resize the image.
$resized = imagecreatetruecolor(static::SIZE, static::SIZE);
imagecopyresampled($resized, $resource, 0, 0, 0, 0, static::SIZE, static::SIZE, imagesx($resource), imagesy($resource));
$resized = $image->resize(static::SIZE, static::SIZE);

// Create an array of greyscale pixel values.
$pixels = [];
for ($y = 0; $y < static::SIZE; $y++) {
for ($x = 0; $x < static::SIZE; $x++) {
$rgb = imagecolorsforindex($resized, imagecolorat($resized, $x, $y));
$pixels[] = floor(($rgb['red'] + $rgb['green'] + $rgb['blue']) / 3);
$rgb = $resized->pickColor($x, $y);
$pixels[] = floor(($rgb[0] + $rgb[1] + $rgb[2]) / 3);
}
}

// Free up memory.
imagedestroy($resized);

// Get the average pixel value.
$average = floor(array_sum($pixels) / count($pixels));

Expand Down
23 changes: 10 additions & 13 deletions src/Implementations/DifferenceHash.php
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
<?php namespace Jenssegers\ImageHash\Implementations;

use Intervention\Image\Image;
use Jenssegers\ImageHash\Implementation;

class DifferenceHash implements Implementation
{
const SIZE = 8;

/**
* {@inheritDoc}
* @inheritdoc
*/
public function hash($resource)
public function hash(Image $image)
{
// For this implementation we create a 8x9 image.
$width = static::SIZE + 1;
$heigth = static::SIZE;
$height = static::SIZE;

// Resize the image.
$resized = imagecreatetruecolor($width, $heigth);
imagecopyresampled($resized, $resource, 0, 0, 0, 0, $width, $heigth, imagesx($resource), imagesy($resource));
$resized = $image->resize($width, $height);

$hash = 0;
$one = 1;
for ($y = 0; $y < $heigth; $y++) {
for ($y = 0; $y < $height; $y++) {
// Get the pixel value for the leftmost pixel.
$rgb = imagecolorsforindex($resized, imagecolorat($resized, 0, $y));
$left = floor(($rgb['red'] + $rgb['green'] + $rgb['blue']) / 3);
$rgb = $resized->pickColor(0, $y);
$left = floor(($rgb[0] + $rgb[1] + $rgb[2]) / 3);

for ($x = 1; $x < $width; $x++) {
// Get the pixel value for each pixel starting from position 1.
$rgb = imagecolorsforindex($resized, imagecolorat($resized, $x, $y));
$right = floor(($rgb['red'] + $rgb['green'] + $rgb['blue']) / 3);
$rgb = $resized->pickColor($x, $y);
$right = floor(($rgb[0] + $rgb[1] + $rgb[2]) / 3);

// Each hash bit is set based on whether the left pixel is brighter than the right pixel.
// http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
Expand All @@ -43,9 +43,6 @@ public function hash($resource)
}
}

// Free up memory.
imagedestroy($resized);

return $hash;
}
}
Loading

0 comments on commit 478fec0

Please sign in to comment.