将图像存储为哈希代码C#

将图像存储为哈希代码C#,c#,.net,image,hash,C#,.net,Image,Hash,我正在建立一个网站,将存储数百万张图片,所以我需要一个唯一的id为每一张图片。什么加密技术最适合存储图像。现在这就是我使用SHA1时代码的样子 sha1旁边是否使用了标准哈希,两个图像是否可能具有相同的哈希代码 Image img = Image.FromFile("image.jpg"); ImageConverter converter = new ImageConverter(); byte[] byteArray = (byte[])converter.ConvertTo(img

我正在建立一个网站,将存储数百万张图片,所以我需要一个唯一的id为每一张图片。什么加密技术最适合存储图像。现在这就是我使用SHA1时代码的样子

sha1旁边是否使用了标准哈希,两个图像是否可能具有相同的哈希代码

 Image img = Image.FromFile("image.jpg");

 ImageConverter converter = new ImageConverter();
 byte[] byteArray = (byte[])converter.ConvertTo(img, typeof(byte[]));

 string hash;

 using (SHA1CryptoServiceProvidersha1 = new SHA1CryptoServiceProvider())
 {
     hash = Convert.ToBase64String(sha1.ComputeHash(byteArray));
 }

如果我理解正确,您希望指定一个SHA1值作为文件名,这样您就可以检测到您的收藏中是否已经有该图像。我不认为这是最好的方法(如果你没有运行数据库,那么它可能是),但是,如果你计划拥有数以百万计的图像(出于实际原因),那么就认为不可能发生碰撞

为此,我不推荐使用SHA256,因为它的两个主要优点(抗碰撞+对某些理论攻击的免疫力)并不值得,因为它的速度大约是SHA1的10倍(并且您将散列大量相当大的文件)

您不必担心它的128位长度:为了有50%的机会在128位中找到冲突,您的集合中需要有18446744073709600000个图像(sqrt为2^128)


哦,我不想听起来很自负,但散列和加密是完全不同的东西。事实上,我想说散列更接近于代码签名/数字签名,而不是密码学。

您可以使用这两种机制

  • 使用GUID作为唯一的文件标识符(文件系统、数据库等)
  • 计算并在图像上存储SHA1或MD5哈希值,并使用该哈希值检查重复项
  • 因此,当上传图像时,您可以使用散列来检查是否存在重复。但是,如果找到一个,那么您可以进行更确定的检查(即检查文件的字节)。现实地说,如果文件不相同,您可能永远不会得到哈希匹配,但是第二次检查将确定


    然后,确定唯一性后,使用文件标识符的GUID或重用现有文件。

    两个不同的图像是否具有相同的哈希代码?不大可能发生的另一方面,同一图像的两个副本可以有不同的散列吗?当然

    取一个无损png,打开它,然后以未压缩的形式重新保存。两个图像的像素将相同,但文件哈希将不同

    除了像素,图像还包含元数据字段,如地理位置、日期/时间、相机制造商、相机型号、ISO速度、焦距等

    因此,在整体使用图像文件时,哈希将受到压缩类型和元数据的影响

    这里的主要问题是:是什么让图片对你来说“独一无二”

    例如,如果一张图像已经上传,那么我下载它,删除相机模型或评论,然后重新上传,它对您来说是不同的图像,还是仍然与原始图像相同?位置字段呢

    如果我下载一个无损png并将其保存为无损tiff,它将具有相同的像素数据,该怎么办

    根据您的要求以及哪些字段很重要,您需要创建相关元数据字段(如果有)与图像的实际未压缩像素数据组合的哈希,而不是使用整个图像文件进行哈希

    System.Security.Cryptography
    中提供的标准哈希算法中,您可能会发现MD5最适合此应用程序。但无论如何都要和不同的人一起玩,看看哪一个最适合你

    下面是一个代码示例,它为元数据字段和图像像素的组合获取哈希:

    public class ImageHash
    {
        public string GetHash(string filePath)
        {
            using (var image = (Bitmap) Image.FromFile(filePath))
                return GetHash(image);
        }
    
        public string GetHash(Bitmap bitmap)
        {
            var formatter = new BinaryFormatter();
    
            using (var memoryStream = new MemoryStream())
            {
                var metafields = GetMetaFields(bitmap).ToArray();
    
                if(metafields.Any())
                    formatter.Serialize(memoryStream, metafields);
    
                var pixelBytes = GetPixelBytes(bitmap);
                memoryStream.Write(pixelBytes, 0, pixelBytes.Length);
    
                using (var hashAlgorithm = GetHashAlgorithm())
                {
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    var hash = hashAlgorithm.ComputeHash(memoryStream);
                    return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
                }
            }
        }
    
        private static HashAlgorithm GetHashAlgorithm() => MD5.Create();
    
        private static byte[] GetPixelBytes(Bitmap bitmap, PixelFormat pixelFormat = PixelFormat.Format32bppRgb)
        {
            var lockedBits = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, pixelFormat);
    
            var bufferSize = lockedBits.Height * lockedBits.Stride;
            var buffer = new byte[bufferSize];
            Marshal.Copy(lockedBits.Scan0, buffer, 0, bufferSize);
    
            bitmap.UnlockBits(lockedBits);
    
            return buffer;
        }
    
        private static IEnumerable<KeyValuePair<string,string>> GetMetaFields(Image image)
        {
            string manufacturer = System.Text.Encoding.ASCII.GetString(image.PropertyItems[1].Value);
    
            yield return new KeyValuePair<string, string>("manufacturer", manufacturer);
            
            // return any other fields you may be interested in
        }
    }
    
    虽然这是一个良好的开端,但该方法有一些方面可以改进,例如:

  • 调整大小后的同一个图像如何?如果这并不能使它成为不同的图片(如中所示,如果需要公差来调整图像大小),那么在散列之前,您需要先将输入图像调整为预先确定的大小

  • 环境光线的变化如何?这会使它成为一幅不同的画面吗?如果答案是否定的,那么您也需要考虑到这一点,并使算法在亮度变化等情况下具有鲁棒性,以便在图像亮度发生变化时仍然产生相同的散列

  • 几何变换呢?e、 例如,如果我在重新上传之前旋转或镜像图像,它是否仍然与原始图像相同?如果是这样,算法需要足够智能,以便在这些类型的转换之后生成相同的散列

  • 您希望如何处理将边框添加到图像中的情况?在图像处理领域有很多这样的场景。其中一些已经有了相当标准的解决方案,而其他许多方案仍在积极研究中

  • 性能:根据图像的数量和大小以及您可以花费多少时间对每个图像进行哈希处理,当前的代码可能会消耗时间和资源。如果您需要它运行得更快和/或占用更少的内存,您可能希望在获取图像哈希之前将图像缩小到预先确定的大小


  • 如果您只需要为图像分配一个唯一的标识符,为什么不分配一个GUID呢?@David我想OP需要存储唯一的图像。在服务器上有两个完全相同的文件,但名称不同是没有意义的。没错,我希望存储唯一的图像。可能两个有效图像具有相同的哈希代码,是的。如果你只关心唯一的文件名,那么就使用GUID。
    var hash = new ImageHash().GetHash(@"some file path");