프로그램(C# etc.)

동일한 이미지를 빠르게 식별하는 알고리즘 feat.c# code

질문폭격 2023. 2. 28. 09:39

 동일한 이미지를 빠르게 식별하는 데 사용할 수 있는 다양한 알고리즘이 있습니다. 일반적으로 사용되는 알고리즘 중 하나는 Perceptual Hashing 알고리즘입니다. 작동 방식은 다음과 같습니다.

  1. 이미지 크기를 고정된 작은 크기(예: 8x8픽셀)로 조정합니다.
  2. 이미지를 회색조로 변환합니다.
  3. 각 이미지의 픽셀에 이산 코사인 변환(DCT)을 적용합니다. DCT는 공간 도메인의 픽셀 값을 주파수 도메인으로 변환합니다.
  4. 각 이미지에 대한 DCT 계수의 평균값을 계산합니다. 이 값은 계수 임계값에 사용됩니다. 계수가 평균보다 크면 1로 설정됩니다. 그렇지 않으면 0으로 설정됩니다.
  5. 임계값 계수를 단일 이진 문자열로 연결합니다. 이 문자열은 이미지의 지각 해시입니다.
  6. 해밍 거리 메트릭을 사용하여 두 이미지의 지각 해시를 비교합니다. 해밍 거리는 두 이진 문자열 간에 다른 비트 수입니다. 해밍 거리가 특정 임계값 미만이면 이미지가 동일한 것으로 간주됩니다.

 이 알고리즘은 이미지당 적은 양의 계산만 필요하기 때문에 빠르고 결과적인 지각 해시는 콤팩트합니다. 그리고 비교하기 쉽습니다. 그러나 완벽하지 않으며 경우에 따라(예: 이미지 크기가 크게 조정되거나 압축된 경우) 모든 동일한 이미지를 식별하지 못할 수 있습니다.

 

Q2. 위 알고리즘을 c#코드로 보여줘

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;

public static class ImageHelper
{
    public static byte[] GetPerceptualHash(Bitmap bitmap)
    {
        const int Size = 8;

        // Resize the image to a fixed size
        Bitmap resized = new Bitmap(bitmap, new Size(Size, Size));

        // Convert the image to grayscale
        Bitmap gray = ToGrayscale(resized);

        // Apply DCT to the pixel values
        double[,] dct = DCT2D(gray);

        // Threshold the DCT coefficients
        double mean = Mean(dct);
        bool[] bits = Threshold(dct, mean);

        // Convert the bits to bytes
        byte[] bytes = BitsToBytes(bits);

        return bytes;
    }

    private static Bitmap ToGrayscale(Bitmap bitmap)
    {
        Bitmap gray = new Bitmap(bitmap.Width, bitmap.Height);

        for (int y = 0; y < bitmap.Height; y++)
        {
            for (int x = 0; x < bitmap.Width; x++)
            {
                Color color = bitmap.GetPixel(x, y);
                byte grayValue = (byte)(0.299 * color.R + 0.587 * color.G + 0.114 * color.B);
                gray.SetPixel(x, y, Color.FromArgb(grayValue, grayValue, grayValue));
            }
        }

        return gray;
    }

    private static double[,] DCT2D(Bitmap bitmap)
    {
        double[,] dct = new double[bitmap.Height, bitmap.Width];

        for (int v = 0; v < bitmap.Height; v++)
        {
            for (int u = 0; u < bitmap.Width; u++)
            {
                double sum = 0.0;

                for (int y = 0; y < bitmap.Height; y++)
                {
                    for (int x = 0; x < bitmap.Width; x++)
                    {
                        Color color = bitmap.GetPixel(x, y);
                        double grayValue = (0.299 * color.R + 0.587 * color.G + 0.114 * color.B) - 128;
                        double cu = (u == 0) ? 1 / Math.Sqrt(2) : 1;
                        double cv = (v == 0) ? 1 / Math.Sqrt(2) : 1;
                        double cosU = Math.Cos(((2 * x + 1) * u * Math.PI) / (2 * bitmap.Width));
                        double cosV = Math.Cos(((2 * y + 1) * v * Math.PI) / (2 * bitmap.Height));
                        double value = cu * cv * grayValue * cosU * cosV;
                        sum += value;
                    }
                }

                double alpha = (u == 0) ? 1 / Math.Sqrt(bitmap.Width) : Math.Sqrt(2) / Math.Sqrt(bitmap.Width);
                double beta = (v == 0) ? 1 / Math.Sqrt(bitmap.Height) : Math.Sqrt(2) / Math.Sqrt(bitmap.Height);
                dct[v, u] = alpha * beta * sum;
            }
        }

        return dct;
    }

    private static double Mean(double[,] dct)
    {
        double sum = 0.0;

        for (int y = 0; y < dct.GetLength(0); y++)
        {
            for (int x = 0; x < dct.GetLength(1); x++)
            {
                sum += dct[y, x];
            }
        }

        return sum / (dct.GetLength(0) * dct.GetLength(1));
	}
    
    private static bool[] Threshold(double[,] dct, double threshold)
    {
        bool[] bits = new bool[dct.GetLength(0) * dct.GetLength(1)];

        for (int y = 0; y < dct.GetLength(0); y++)
        {
            for (int x = 0; x < dct.GetLength(1); x++)
            {
                bits[y * dct.GetLength(1) + x] = (dct[y, x] > threshold);
            }
        }

        return bits;
    }

    private static byte[] BitsToBytes(bool[] bits)
    {
        byte[] bytes = new byte[bits.Length / 8];

        for (int i = 0; i < bytes.Length; i++)
        {
            byte value = 0;

            for (int j = 0; j < 8; j++)
            {
                if (bits[i * 8 + j])
                {
                    value |= (byte)(1 << (7 - j));
                }
            }

            bytes[i] = value;
        }

        return bytes;
    }

    public static int HammingDistance(byte[] hash1, byte[] hash2)
    {
        int distance = 0;

        for (int i = 0; i < hash1.Length; i++)
        {
            byte xor = (byte)(hash1[i] ^ hash2[i]);

            for (int j = 0; j < 8; j++)
            {
                if ((xor & (1 << (7 - j))) != 0)
                {
                    distance++;
                }
            }
        }

        return distance;
    }
}

 

Q2. 위 코드 사용해서 jpg파일 2개 비교하는 함수 보여줘

using System;
using System.Drawing;
using System.IO;

public static class ImageComparison
{
    public static bool AreImagesIdentical(string filePath1, string filePath2, int maxHammingDistance)
    {
        // Load the images from the files
        Bitmap bitmap1 = new Bitmap(filePath1);
        Bitmap bitmap2 = new Bitmap(filePath2);

        // Compute the perceptual hashes of the images
        byte[] hash1 = ImageHelper.GetPerceptualHash(bitmap1);
        byte[] hash2 = ImageHelper.GetPerceptualHash(bitmap2);

        // Compute the Hamming distance between the hashes
        int distance = ImageHelper.HammingDistance(hash1, hash2);

        // Compare the Hamming distance to the threshold
        return (distance <= maxHammingDistance);
    }
}