1. 傅里葉變換公式

2. 歐拉公式

簡化

3. 簡單推導
將傅里葉變換公式拆開可以獲得

DFT變換的結果:
實數部分為

虛數部分為

設

則可以轉換為
實數部分

虛數系數部分

由于cos是一個偶函式,sin是一個奇函式
奇函式 + 奇函式=奇函式(相減一樣)
偶函式 + 偶函式=偶函式(相減一樣)
奇函式 * 奇函式=偶函式(相除一樣)
偶函式 * 偶函式=偶函式(相除一樣)
奇函式 * 偶函式=奇函式(相除一樣)

因此當X[n]是一個實數函式時,其頻域的實部是偶函式,虛部是一個奇函式,那么假設原信號X[n]是一個全身實數的偶函式信號會怎么樣?
X[n]sin(kt)是一個奇函式所以

所以當原時域信號是一個實偶信號時,我們就可以把DFT寫成

DCT變換就是DFT變換的一種特殊形式,其特殊點在于其原始變換信號是一個實偶函式,但是實際應用中并沒有那么多實偶函式信號給我們,因此為了適用面更廣,自然界沒有那么多實偶信號,我們可以用用實信號造一個,
設一長度為N的實數離散信號{x[0],x[1],x[2]…x[N-1]}首先,先將這個信號長度擴大成原來的兩倍,并變成2N,定義新信號x’[m]為
x’[m] = x[m] (0<=m<=N-1)
x’[m] = x[-m-1] (-N<=m<=-1)
所以DFT公式由于范圍變成了[-N,N-1]

但是,這樣的插值之后也隨之帶來了一個問題,這個信號并不關于m=0偶對稱,它關于m = -1/2對稱,因此為了讓信號仍然關于原點對稱,把整個信號向右平移1/2個單位得到

之后根據歐拉公式對其展開,由于已經證明虛部為0所以只需要實數部分

這個序列是一個偶對稱序列繼續變形

設n = m - 1/2 帶入上式

將C(u)帶入得到DCT公式,
4. PHash
流程:
-
灰度化
-
縮小尺寸 => 8x8 px
-
二值化: 基于不同的hash演算法
-
DCT變換
計算DCT 計算DCT均值 -
哈希值計算,將每個DCT值,與平均值進行比較,大于或等于平均值,記為1,小 于平均值,記為0,由此生成二進制陣列,
-
比較距離(相似性) = 比較fingerprint
5. 代碼
- 編譯器部分
namespace PicDifficulty
{
public class PicDifficulty : EditorWindow
{
[MenuItem("MyTool/PicDifficulty")]
public static void showPicDifficultyWindow()
{
var window = (PicDifficulty)GetWindow(typeof(PicDifficulty));
window.Show();
}
private Texture2D E_Texture;
private Texture2D E_Texture1;
public void OnGUI()
{
EditorGUILayout.BeginVertical();
E_Texture = EditorGUILayout.ObjectField("貼圖", E_Texture, typeof(Texture), true) as Texture2D;
E_Texture1 = EditorGUILayout.ObjectField("貼圖1", E_Texture1, typeof(Texture), true) as Texture2D;
if (GUILayout.Button("轉換"))
{
Debug.LogError(pHash.PicSimilarity(E_Texture, E_Texture1));
}
EditorGUILayout.EndVertical();
}
- PHash部分
using PicDifficulty;
using UnityEngine;
public static class pHash
{
/// <summary>
/// 壓縮圖片為8x8px
/// </summary>
/// <param name="tex"></param>
/// <param name="size"></param>
/// <returns></returns>
public static Texture2D ReduceSize(Texture2D tex, int size)
{
if (tex == null || size <= 0)
{
Debug.LogError("圖片或縮放比例錯誤");
return null;
}
Texture2D newTex = new Texture2D(size, size, TextureFormat.RGBA32, false);
//比率
float ratioW = tex.width / size;
float ratioH = tex.height / size;
Color color;
for (int h = 0; h < newTex.height; h++)
{
for (int w = 0; w < newTex.width; w++)
{
color = tex.GetPixel(Mathf.RoundToInt(w * ratioW), Mathf.RoundToInt(h * ratioH));
newTex.SetPixel(w, h, color);
}
}
newTex.Apply();
return newTex;
}
/// <summary>
/// 將8x8px的圖片轉換為灰度圖
/// </summary>
/// <param name="tex"></param>
/// <returns></returns>
public static Texture2D Tex2Gray(Texture2D tex)
{
Texture2D newTex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
Color color;
for (int h = 0; h < tex.height; h++)
{
for (int w = 0; w < tex.width; w++)
{
color = tex.GetPixel(w, h);
float gray = (color.r * 29.9f + color.g * 58.7f + color.b * 11.4f) / 100;
newTex.SetPixel(w, h, new Color(gray, gray, gray));
}
}
newTex.Apply();
return newTex;
}
public static bool PicSimilarity(Texture2D E_Texture, Texture2D E_Texture1)
{
if (E_Texture == null || E_Texture1 == null)
{
Debug.AssertFormat(!(E_Texture == null || E_Texture1 == null), "請檢查是否有圖片為空");
return false;
}
var tex64 = ReduceSize(E_Texture, 8);
var texGray = Tex2Gray(tex64);
var texFloat = DCT.image2F(texGray);
float[,] DCTMatrix = Matrix.Multiply(Matrix.Multiply(DCT.DCTMatrix, texFloat), DCT.DCTMatrixT);
float aver = DCT.averageDCT(DCTMatrix);
string hash = DCT.getHash(DCTMatrix, aver);
var tex641 = ReduceSize(E_Texture1, 8);
var texGray1 = Tex2Gray(tex641);
var texFloat1 = DCT.image2F(texGray1);
float[,] DCTMatrix1 = Matrix.Multiply(Matrix.Multiply(DCT.DCTMatrix, texFloat1), DCT.DCTMatrixT);
float aver1 = DCT.averageDCT(DCTMatrix1);
string hash1 = DCT.getHash(DCTMatrix1, aver1);
float dis = DCT.computeDistance(hash, hash1);
Debug.LogError(dis);
return dis >= 0.6 ? true : false;
}
}
- DCT部分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PicDifficulty
{
public class DCT
{
public static float[,] DCTMatrix => creatDCTMatrix(8);
public static float[,] DCTMatrixT => Matrix.Transpose(DCTMatrix);
//圖片轉矩陣
public static float[,] image2F(Texture2D tex)
{
int size = tex.width;
float[,] f = new float[size, size];
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
f[i, j] = tex.GetPixel(i, j).r;
}
}
return f;
}
//計算DCT矩陣
public static float[,] creatDCTMatrix(int size)
{
float[,] ret = new float[size, size];
for (int x = 0; x < size; x++)
{
for (int y = 0; y < size; y++)
{
float angle = ((y + 0.5f) * Mathf.PI * x / size);
ret[x, y] = cfunc(x, size) * (float)Mathf.Cos(angle);
}
}
return ret;
}
static float cfunc(int n, int size)
{
if (n == 0)
return Mathf.Sqrt(1f / size);
else
return Mathf.Sqrt(2f / size);
}
//DCT均值
public static float averageDCT(float[,] dct)
{
int size = dct.GetLength(0);
float aver = 0;
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
aver += dct[i, j];
}
}
return aver / (size * size);
}
//獲取當前圖片pHash值
public static string getHash(float[,] dct, float aver)
{
string hash = string.Empty;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
hash += (dct[i, j] >= aver ? "1" : "0");
}
}
return hash;
}
//計算兩圖片哈希值的漢明距離
public static float computeDistance(string hash1, string hash2)
{
float dis = 0;
for (int i = 0; i < hash1.Length; i++)
{
if (hash1[i] == hash2[i])
{
dis++;
}
}
return dis / hash1.Length;
}
}
}
- 矩陣轉置和矩陣乘法
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace PicDifficulty
{
public static class Matrix
{
//矩陣轉置
public static float[,] Transpose(float[,] C)
{
int size = C.GetLength(0);
float[,] ret = new float[size, size];
for (var x = 0; x < size; x++)
{
for (var y = 0; y < size; y++)
{
ret[y, x] = C[x, y];
}
}
return ret;
}
//矩陣相乘
public static float[,] Multiply(float[,] C1, float[,] C2)
{
int size = C1.GetLength(0);
float[,] ret = new float[size, size];
for (var y = 0; y < size; y++)
{
for (var x = 0; x < size; x++)
{
float sum1 = 0;
for (int k = 0; k < size; k++)
{
sum1 += C1[x, k] * C2[k, y];
}
ret[x, y] = sum1;
}
}
return ret;
}
}
}
6. 參考
https://zhuanlan.zhihu.com/p/85299446 --詳解離散余弦變換(DCT)
https://blog.csdn.net/Getyoufly/article/details/108254558 --Unity C#使用pHash演算法實作圖片相似度計算
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/382968.html
標籤:其他
上一篇:嵌入式實時作業系統5——就緒表
