Browse Source

扩展图像哈希功能并支持多种算法

懒得勤快 3 weeks ago
parent
commit
b0e8a69593

+ 1 - 1
Directory.Build.props

@@ -1,6 +1,6 @@
 <Project>
  <PropertyGroup>
-   <Version>2025.5.3</Version>
+   <Version>2025.5.4</Version>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
 </Project>

+ 2 - 2
Masuit.Tools.Abstractions/Media/ImageHasher.cs

@@ -711,10 +711,10 @@ public class ImageHasher
         var hashDifference = hash1 ^ hash2;
 
         // 计算汉明距离
-        var onesInHash = HammingWeight(hashDifference);
+        var hamming = HammingWeight(hashDifference);
 
         // 得到相似度
-        return 1.0f - onesInHash / 64.0f;
+        return 1.0f - hamming / 64.0f;
     }
 
     /// <summary>

+ 154 - 8
Masuit.Tools.Abstractions/Media/ImageSharpTransformer.cs

@@ -100,6 +100,17 @@ public static class ImageHashExt
         return hasher.AverageHash64(image);
     }
 
+    /// <summary>
+    /// 使用平均值算法计算图像的64位哈希
+    /// </summary>
+    /// <param name="image">读取到的图片流</param>
+    /// <returns>64位hash值</returns>
+    public static ulong AverageHash64(this Image<L8> image)
+    {
+        var hasher = new ImageHasher();
+        return hasher.AverageHash64(image);
+    }
+
     /// <summary>
     /// 使用中值算法计算给定图像的64位哈希
     /// 将图像转换为8x8灰度图像,从中查找中值像素值,然后在结果哈希中将值大于中值的所有像素标记为1。与基于平均值的实现相比,更能抵抗非线性图像编辑。
@@ -112,6 +123,18 @@ public static class ImageHashExt
         return hasher.MedianHash64(image);
     }
 
+    /// <summary>
+    /// 使用中值算法计算给定图像的64位哈希
+    /// 将图像转换为8x8灰度图像,从中查找中值像素值,然后在结果哈希中将值大于中值的所有像素标记为1。与基于平均值的实现相比,更能抵抗非线性图像编辑。
+    /// </summary>
+    /// <param name="image">读取到的图片流</param>
+    /// <returns>64位hash值</returns>
+    public static ulong MedianHash64(this Image<L8> image)
+    {
+        var hasher = new ImageHasher();
+        return hasher.MedianHash64(image);
+    }
+
     /// <summary>
     /// 使用中值算法计算给定图像的256位哈希
     /// 将图像转换为16x16的灰度图像,从中查找中值像素值,然后在结果哈希中将值大于中值的所有像素标记为1。与基于平均值的实现相比,更能抵抗非线性图像编辑。
@@ -124,6 +147,18 @@ public static class ImageHashExt
         return hasher.MedianHash256(image);
     }
 
+    /// <summary>
+    /// 使用中值算法计算给定图像的256位哈希
+    /// 将图像转换为16x16的灰度图像,从中查找中值像素值,然后在结果哈希中将值大于中值的所有像素标记为1。与基于平均值的实现相比,更能抵抗非线性图像编辑。
+    /// </summary>
+    /// <param name="image">读取到的图片流</param>
+    /// <returns>256位hash值,生成一个4长度的数组返回</returns>
+    public static ulong[] MedianHash256(this Image<L8> image)
+    {
+        var hasher = new ImageHasher();
+        return hasher.MedianHash256(image);
+    }
+
     /// <summary>
     /// 使用差分哈希算法计算图像的64位哈希。
     /// </summary>
@@ -136,6 +171,18 @@ public static class ImageHashExt
         return hasher.DifferenceHash64(image);
     }
 
+    /// <summary>
+    /// 使用差分哈希算法计算图像的64位哈希。
+    /// </summary>
+    /// <see cref="https://segmentfault.com/a/1190000038308093"/>
+    /// <param name="image">读取到的图片流</param>
+    /// <returns>64位hash值</returns>
+    public static ulong DifferenceHash64(this Image<L8> image)
+    {
+        var hasher = new ImageHasher();
+        return hasher.DifferenceHash64(image);
+    }
+
     /// <summary>
     /// 使用差分哈希算法计算图像的64位哈希。
     /// </summary>
@@ -148,6 +195,18 @@ public static class ImageHashExt
         return hasher.DifferenceHash256(image);
     }
 
+    /// <summary>
+    /// 使用差分哈希算法计算图像的64位哈希。
+    /// </summary>
+    /// <see cref="https://segmentfault.com/a/1190000038308093"/>
+    /// <param name="image">读取到的图片流</param>
+    /// <returns>256位hash值</returns>
+    public static ulong[] DifferenceHash256(this Image<L8> image)
+    {
+        var hasher = new ImageHasher();
+        return hasher.DifferenceHash256(image);
+    }
+
     /// <summary>
     /// 使用DCT算法计算图像的64位哈希
     /// </summary>
@@ -160,18 +219,56 @@ public static class ImageHashExt
         return hasher.DctHash(image);
     }
 
+    /// <summary>
+    /// 使用DCT算法计算图像的64位哈希
+    /// </summary>
+    /// <see cref="https://segmentfault.com/a/1190000038308093"/>
+    /// <param name="image">读取到的图片流</param>
+    /// <returns>64位hash值</returns>
+    public static ulong DctHash(this Image<L8> image)
+    {
+        var hasher = new ImageHasher();
+        return hasher.DctHash(image);
+    }
+
     /// <summary>
     /// 使用汉明距离比较两幅图像的哈希值。结果1表示图像完全相同,而结果0表示图像完全不同。
     /// </summary>
     /// <param name="image1">图像1</param>
     /// <param name="image2">图像2</param>
+    /// <param name="algorithm">对比算法</param>
     /// <returns>相似度范围:[0,1]</returns>
-    public static float Compare(this Image image1, Image image2)
+    public static float Compare(this Image image1, Image image2, ImageHashAlgorithm algorithm = ImageHashAlgorithm.Difference)
     {
         var hasher = new ImageHasher();
-        var hash1 = hasher.DifferenceHash256(image1);
-        var hash2 = hasher.DifferenceHash256(image2);
-        return ImageHasher.Compare(hash1, hash2);
+        return algorithm switch
+        {
+            ImageHashAlgorithm.Average => ImageHasher.Compare(hasher.AverageHash64(image1), hasher.AverageHash64(image2)),
+            ImageHashAlgorithm.Medium => ImageHasher.Compare(hasher.MedianHash256(image1), hasher.MedianHash256(image2)),
+            ImageHashAlgorithm.Difference => ImageHasher.Compare(hasher.DifferenceHash256(image1), hasher.DifferenceHash256(image2)),
+            ImageHashAlgorithm.DCT => ImageHasher.Compare(hasher.DctHash(image1), hasher.DctHash(image2)),
+            _ => throw new ArgumentOutOfRangeException(nameof(algorithm), algorithm, null)
+        };
+    }
+
+    /// <summary>
+    /// 使用汉明距离比较两幅图像的哈希值。结果1表示图像完全相同,而结果0表示图像完全不同。
+    /// </summary>
+    /// <param name="image1">图像1</param>
+    /// <param name="image2">图像2</param>
+    /// <param name="algorithm">对比算法</param>
+    /// <returns>相似度范围:[0,1]</returns>
+    public static float Compare(this Image<L8> image1, Image<L8> image2, ImageHashAlgorithm algorithm = ImageHashAlgorithm.Difference)
+    {
+        var hasher = new ImageHasher();
+        return algorithm switch
+        {
+            ImageHashAlgorithm.Average => ImageHasher.Compare(hasher.AverageHash64(image1), hasher.AverageHash64(image2)),
+            ImageHashAlgorithm.Medium => ImageHasher.Compare(hasher.MedianHash256(image1), hasher.MedianHash256(image2)),
+            ImageHashAlgorithm.Difference => ImageHasher.Compare(hasher.DifferenceHash256(image1), hasher.DifferenceHash256(image2)),
+            ImageHashAlgorithm.DCT => ImageHasher.Compare(hasher.DctHash(image1), hasher.DctHash(image2)),
+            _ => throw new ArgumentOutOfRangeException(nameof(algorithm), algorithm, null)
+        };
     }
 
     /// <summary>
@@ -179,12 +276,61 @@ public static class ImageHashExt
     /// </summary>
     /// <param name="image1">图像1的hash</param>
     /// <param name="image2path">图像2的路径</param>
+    /// <param name="algorithm">对比算法</param>
     /// <returns>相似度范围:[0,1]</returns>
-    public static float Compare(this Image image1, string image2path)
+    public static float Compare(this Image image1, string image2path, ImageHashAlgorithm algorithm = ImageHashAlgorithm.Difference)
     {
         var hasher = new ImageHasher();
-        var hash1 = hasher.DifferenceHash256(image1);
-        var hash2 = hasher.DifferenceHash256(image2path);
-        return ImageHasher.Compare(hash1, hash2);
+        return algorithm switch
+        {
+            ImageHashAlgorithm.Average => ImageHasher.Compare(hasher.AverageHash64(image1), hasher.AverageHash64(image2path)),
+            ImageHashAlgorithm.Medium => ImageHasher.Compare(hasher.MedianHash256(image1), hasher.MedianHash256(image2path)),
+            ImageHashAlgorithm.Difference => ImageHasher.Compare(hasher.DifferenceHash256(image1), hasher.DifferenceHash256(image2path)),
+            ImageHashAlgorithm.DCT => ImageHasher.Compare(hasher.DctHash(image1), hasher.DctHash(image2path)),
+            _ => throw new ArgumentOutOfRangeException(nameof(algorithm), algorithm, null)
+        };
     }
+
+    /// <summary>
+    /// 使用汉明距离比较两幅图像的哈希值。结果1表示图像完全相同,而结果0表示图像完全不同。
+    /// </summary>
+    /// <param name="image1">图像1的hash</param>
+    /// <param name="image2path">图像2的路径</param>
+    /// <param name="algorithm">对比算法</param>
+    /// <returns>相似度范围:[0,1]</returns>
+    public static float Compare(this Image<L8> image1, string image2path, ImageHashAlgorithm algorithm = ImageHashAlgorithm.Difference)
+    {
+        var hasher = new ImageHasher();
+        return algorithm switch
+        {
+            ImageHashAlgorithm.Average => ImageHasher.Compare(hasher.AverageHash64(image1), hasher.AverageHash64(image2path)),
+            ImageHashAlgorithm.Medium => ImageHasher.Compare(hasher.MedianHash256(image1), hasher.MedianHash256(image2path)),
+            ImageHashAlgorithm.Difference => ImageHasher.Compare(hasher.DifferenceHash256(image1), hasher.DifferenceHash256(image2path)),
+            ImageHashAlgorithm.DCT => ImageHasher.Compare(hasher.DctHash(image1), hasher.DctHash(image2path)),
+            _ => throw new ArgumentOutOfRangeException(nameof(algorithm), algorithm, null)
+        };
+    }
+}
+
+public enum ImageHashAlgorithm
+{
+    /// <summary>
+    /// 均值算法
+    /// </summary>
+    Average,
+
+    /// <summary>
+    /// 中值算法
+    /// </summary>
+    Medium,
+
+    /// <summary>
+    /// 差异算法
+    /// </summary>
+    Difference,
+
+    /// <summary>
+    /// DCT算法
+    /// </summary>
+    DCT
 }