Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ["8.2", "8.3", "8.4"]
php-version: ["8.3", "8.4"]
Comment thread
olivervogel marked this conversation as resolved.
extension: ["gd, :imagick", "imagick, :gd"] # the ':" prefix is used to unload the other extension
name: PHP ${{ matrix.php-version }} - ${{ matrix.extension }}
steps:
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
}
],
"require": {
"php": "^8.2",
"intervention/image": "^3.3"
"php": "^8.3",
"intervention/image": "^4.0"
},
"require-dev": {
"phpunit/phpunit": "^11.5.3",
Expand Down
15 changes: 8 additions & 7 deletions src/ImageHash.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@

use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
use Intervention\Image\Drivers\Gd\Driver as GdDriver;
use Intervention\Image\Image;
use Intervention\Image\ImageManager;
use Intervention\Image\Interfaces\ImageInterface;
use Intervention\Image\Interfaces\ImageManagerInterface;
use Jenssegers\ImageHash\Implementations\DifferenceHash;
use RuntimeException;

class ImageHash
{
protected Implementation $implementation;

private ImageManager $driver;
private ImageManagerInterface $driver;

public function __construct(
?Implementation $implementation = null,
?ImageManager $driver = null
?ImageManagerInterface $driver = null
) {
$this->implementation = $implementation ?: $this->defaultImplementation();
$this->driver = $driver ?: $this->defaultDriver();
Expand All @@ -28,7 +29,7 @@ public function __construct(
*/
public function hash(mixed $image): Hash
{
$image = $this->driver->read($image);
$image = $this->driver->decode($image);

return $this->implementation->hash($image);
}
Expand All @@ -52,17 +53,17 @@ public function distance(Hash $hash1, Hash $hash2): int
return $hash1->distance($hash2);
}

protected function createResource(string $data): Image
protected function createResource(string $data): ImageInterface
{
return $this->driver->read($data);
return $this->driver->decode($data);
}

protected function defaultImplementation(): Implementation
{
return new DifferenceHash();
}

protected function defaultDriver(): ImageManager
protected function defaultDriver(): ImageManagerInterface
Comment thread
olivervogel marked this conversation as resolved.
{
if (extension_loaded('imagick')) {
return new ImageManager(new ImagickDriver());
Expand Down
4 changes: 2 additions & 2 deletions src/Implementation.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php namespace Jenssegers\ImageHash;

use Intervention\Image\Image;
use Intervention\Image\Interfaces\ImageInterface;

interface Implementation
{
public function hash(Image $image): Hash;
public function hash(ImageInterface $image): Hash;
}
7 changes: 4 additions & 3 deletions src/Implementations/AverageHash.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php namespace Jenssegers\ImageHash\Implementations;

use Intervention\Image\Image;
use Intervention\Image\Interfaces\ImageInterface;
use Jenssegers\ImageHash\Hash;
use Jenssegers\ImageHash\Implementation;
use Jenssegers\ImageHash\RgbArrayAnalyzer;

class AverageHash implements Implementation
{
Expand All @@ -13,7 +14,7 @@ public function __construct(int $size = 8)
$this->size = $size;
}

public function hash(Image $image): Hash
public function hash(ImageInterface $image): Hash
{
// Resize the image.
$resized = $image->resize($this->size, $this->size);
Expand All @@ -22,7 +23,7 @@ public function hash(Image $image): Hash
$pixels = [];
for ($y = 0; $y < $this->size; $y++) {
for ($x = 0; $x < $this->size; $x++) {
$rgb = $resized->pickColor($x, $y)->toArray();
$rgb = $resized->analyze(new RgbArrayAnalyzer($x, $y));
$pixels[] = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114));
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/Implementations/BlockHash.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

namespace Jenssegers\ImageHash\Implementations;

use Intervention\Image\Image;
use Intervention\Image\Interfaces\ImageInterface;
use InvalidArgumentException;
use Jenssegers\ImageHash\Hash;
use Jenssegers\ImageHash\Implementation;
use Jenssegers\ImageHash\RgbArrayAnalyzer;

class BlockHash implements Implementation
{
Expand Down Expand Up @@ -37,7 +38,7 @@ public function __construct(int $size = 16, $mode = self::PRECISE)
$this->mode = $mode;
}

public function hash(Image $image): Hash
public function hash(ImageInterface $image): Hash
{
if ($this->mode === self::QUICK) {
return $this->even($image);
Expand All @@ -46,7 +47,7 @@ public function hash(Image $image): Hash
return $this->uneven($image);
}

private function even(Image $image): Hash
private function even(ImageInterface $image): Hash
{
$width = $image->width();
$height = $image->height();
Expand All @@ -63,7 +64,8 @@ private function even(Image $image): Hash
for ($ix = 0; $ix < $blocksizeX; $ix++) {
$cx = $x * $blocksizeX + $ix;
$cy = $y * $blocksizeY + $iy;
$rgb = $image->pickColor($cx, $cy)->toArray();
$rgb = $image->analyze(new RgbArrayAnalyzer($cx, $cy));

$value += $rgb[0] + $rgb[1] + $rgb[2];
}
}
Expand All @@ -75,7 +77,7 @@ private function even(Image $image): Hash
return $this->blocksToBits($result, $blocksizeX * $blocksizeY);
}

private function uneven(Image $image): Hash
private function uneven(ImageInterface $image): Hash
{
$imageWidth = $image->width();
$imageHeight = $image->height();
Expand Down Expand Up @@ -114,7 +116,7 @@ private function uneven(Image $image): Hash
}

for ($x = 0; $x < $imageWidth; $x++) {
$rgb = $image->pickColor($x, $y)->toArray();
$rgb = $image->analyze(new RgbArrayAnalyzer($x, $y));
$value = $rgb[0] + $rgb[1] + $rgb[2];

if ($evenX) {
Expand Down
9 changes: 5 additions & 4 deletions src/Implementations/DifferenceHash.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php namespace Jenssegers\ImageHash\Implementations;

use Intervention\Image\Image;
use Intervention\Image\Interfaces\ImageInterface;
use Jenssegers\ImageHash\Hash;
use Jenssegers\ImageHash\Implementation;
use Jenssegers\ImageHash\RgbArrayAnalyzer;

class DifferenceHash implements Implementation
{
Expand All @@ -13,7 +14,7 @@ public function __construct(int $size = 8)
$this->size = $size;
}

public function hash(Image $image): Hash
public function hash(ImageInterface $image): Hash
{
// For this implementation we create a 8x9 image.
$width = $this->size + 1;
Expand All @@ -25,12 +26,12 @@ public function hash(Image $image): Hash
$bits = [];
for ($y = 0; $y < $height; $y++) {
// Get the pixel value for the leftmost pixel.
$rgb = $resized->pickColor(0, $y)->toArray();
$rgb = $resized->analyze(new RgbArrayAnalyzer(0, $y));
$left = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114));

for ($x = 1; $x < $width; $x++) {
// Get the pixel value for each pixel starting from position 1.
$rgb = $resized->pickColor($x, $y)->toArray();
$rgb = $resized->analyze(new RgbArrayAnalyzer($x, $y));
$right = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114));

// Each hash bit is set based on whether the left pixel is brighter than the right pixel.
Expand Down
7 changes: 4 additions & 3 deletions src/Implementations/PerceptualHash.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?php namespace Jenssegers\ImageHash\Implementations;

use Intervention\Image\Image;
use Intervention\Image\Interfaces\ImageInterface;
use InvalidArgumentException;
use Jenssegers\ImageHash\Hash;
use Jenssegers\ImageHash\Implementation;
use Jenssegers\ImageHash\RgbArrayAnalyzer;

class PerceptualHash implements Implementation
{
Expand Down Expand Up @@ -31,7 +32,7 @@ public function __construct(int $size = 32, string $comparisonMethod = self::AVE
$this->comparisonMethod = $comparisonMethod;
}

public function hash(Image $image): Hash
public function hash(ImageInterface $image): Hash
{
// Resize the image.
$resized = $image->resize($this->size, $this->size);
Expand All @@ -43,7 +44,7 @@ public function hash(Image $image): Hash

for ($y = 0; $y < $this->size; $y++) {
for ($x = 0; $x < $this->size; $x++) {
$rgb = $resized->pickColor($x, $y)->toArray();
$rgb = $resized->analyze(new RgbArrayAnalyzer($x, $y));
$row[$x] = (int) floor(($rgb[0] * 0.299) + ($rgb[1] * 0.587) + ($rgb[2] * 0.114));
}
$rows[$y] = $this->calculateDCT($row);
Expand Down
29 changes: 29 additions & 0 deletions src/RgbArrayAnalyzer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Jenssegers\ImageHash;

use Intervention\Image\Colors\Rgb\Colorspace as Rgb;
use Intervention\Image\Interfaces\AnalyzerInterface;
use Intervention\Image\Interfaces\ColorChannelInterface;
use Intervention\Image\Interfaces\ImageInterface;

class RgbArrayAnalyzer implements AnalyzerInterface
{
public function __construct(protected int $x, protected int $y)
{
//
}

/**
* Return an array of the rgb color channel values of the color at the current position.
*/
public function analyze(ImageInterface $image): mixed
{
return array_map(
fn(ColorChannelInterface $channel): int => (int) $channel->value(),
$image->colorAt($this->x, $this->y)->toColorspace(Rgb::class)->channels()
);
}
}
4 changes: 2 additions & 2 deletions tests/ImageHashTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

use Intervention\Image\Exceptions\DecoderException;
use Intervention\Image\Exceptions\RuntimeException;
use Jenssegers\ImageHash\ImageHash;
use PHPUnit\Framework\TestCase;

Expand All @@ -15,7 +15,7 @@ public function setup(): void

public function testHashInvalidFile()
{
$this->expectException(DecoderException::class);
$this->expectException(RuntimeException::class);

$this->imageHash->hash('nonImageString');
}
Expand Down