Update
This commit is contained in:
100
app/Helpers/FirstParty/ImageHash/ImageHashService.php
Normal file
100
app/Helpers/FirstParty/ImageHash/ImageHashService.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers\FirstParty\ImageHash;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Jenssegers\ImageHash\ImageHash;
|
||||
use Jenssegers\ImageHash\Implementations\DifferenceHash;
|
||||
|
||||
class ImageHashService
|
||||
{
|
||||
private ImageHash $hasher;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->hasher = new ImageHash(new DifferenceHash);
|
||||
}
|
||||
|
||||
public function generateHashFromUrl(string $url): ?string
|
||||
{
|
||||
try {
|
||||
$response = Http::timeout(30)->get($url);
|
||||
|
||||
if (! $response->successful()) {
|
||||
Log::warning("Failed to download image from URL: {$url}");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$imageData = $response->body();
|
||||
|
||||
return $this->generateHashFromData($imageData);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Error generating hash from URL {$url}: ".$e->getMessage());
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function generateHashFromData(string $imageData): ?string
|
||||
{
|
||||
try {
|
||||
$tempFile = tempnam(sys_get_temp_dir(), 'imagehash_');
|
||||
file_put_contents($tempFile, $imageData);
|
||||
|
||||
$hash = $this->hasher->hash($tempFile);
|
||||
unlink($tempFile);
|
||||
|
||||
return $hash->toHex();
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error generating hash from image data: '.$e->getMessage());
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function calculateHammingDistance(string $hash1, string $hash2): int
|
||||
{
|
||||
// Validate hashes are not empty
|
||||
if (empty($hash1) || empty($hash2)) {
|
||||
return PHP_INT_MAX; // Return max distance for invalid hashes
|
||||
}
|
||||
|
||||
// Pad shorter hash with zeros to make them equal length
|
||||
$maxLength = max(strlen($hash1), strlen($hash2));
|
||||
$hash1 = str_pad($hash1, $maxLength, '0', STR_PAD_LEFT);
|
||||
$hash2 = str_pad($hash2, $maxLength, '0', STR_PAD_LEFT);
|
||||
|
||||
$distance = 0;
|
||||
for ($i = 0; $i < $maxLength; $i++) {
|
||||
if ($hash1[$i] !== $hash2[$i]) {
|
||||
$distance++;
|
||||
}
|
||||
}
|
||||
|
||||
return $distance;
|
||||
}
|
||||
|
||||
public function areHashesSimilar(string $hash1, string $hash2, int $threshold = 5): bool
|
||||
{
|
||||
return $this->calculateHammingDistance($hash1, $hash2) <= $threshold;
|
||||
}
|
||||
|
||||
public function findSimilarHashes(string $targetHash, array $hashes, int $threshold = 5): array
|
||||
{
|
||||
$similar = [];
|
||||
|
||||
foreach ($hashes as $id => $hash) {
|
||||
if ($this->areHashesSimilar($targetHash, $hash, $threshold)) {
|
||||
$similar[$id] = $this->calculateHammingDistance($targetHash, $hash);
|
||||
}
|
||||
}
|
||||
|
||||
asort($similar);
|
||||
|
||||
return $similar;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user