프로그램(C# etc.)
동일한 이미지를 빠르게 식별하는 알고리즘 feat.c# code
질문폭격
2023. 2. 28. 09:39
동일한 이미지를 빠르게 식별하는 데 사용할 수 있는 다양한 알고리즘이 있습니다. 일반적으로 사용되는 알고리즘 중 하나는 Perceptual Hashing 알고리즘입니다. 작동 방식은 다음과 같습니다.
- 이미지 크기를 고정된 작은 크기(예: 8x8픽셀)로 조정합니다.
- 이미지를 회색조로 변환합니다.
- 각 이미지의 픽셀에 이산 코사인 변환(DCT)을 적용합니다. DCT는 공간 도메인의 픽셀 값을 주파수 도메인으로 변환합니다.
- 각 이미지에 대한 DCT 계수의 평균값을 계산합니다. 이 값은 계수 임계값에 사용됩니다. 계수가 평균보다 크면 1로 설정됩니다. 그렇지 않으면 0으로 설정됩니다.
- 임계값 계수를 단일 이진 문자열로 연결합니다. 이 문자열은 이미지의 지각 해시입니다.
- 해밍 거리 메트릭을 사용하여 두 이미지의 지각 해시를 비교합니다. 해밍 거리는 두 이진 문자열 간에 다른 비트 수입니다. 해밍 거리가 특정 임계값 미만이면 이미지가 동일한 것으로 간주됩니다.
이 알고리즘은 이미지당 적은 양의 계산만 필요하기 때문에 빠르고 결과적인 지각 해시는 콤팩트합니다. 그리고 비교하기 쉽습니다. 그러나 완벽하지 않으며 경우에 따라(예: 이미지 크기가 크게 조정되거나 압축된 경우) 모든 동일한 이미지를 식별하지 못할 수 있습니다.
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);
}
}