This commit is contained in:
ct
2025-07-15 20:03:10 +08:00
parent b54e4f2092
commit 096f515f58
15 changed files with 1161 additions and 3 deletions

View 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;
}
}